2016-06-19 12 views
1

Ich spiele mit Epoll unter Linux zum ersten Mal und sehe ein seltsames Verhalten. Wenn ich mich mit einem Client mit einem Socket verbinde, sehe ich zwei Ereignisse, die von epoll_wait auf der Serverseite ausgegeben werden. Wenn ich beim zweiten Versuch accept anrufe, bekomme ich einen "temporary unavailable" -Fehler.Sehen zwei akzeptieren Ereignisse auf epoll

Hier ist der einfache Client-Code:

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/socket.h> 
#include <sys/un.h> 
#include <unistd.h> 

const char* msg = "friendly ping"; 

int main(int argc, char** argv) { 
    if (argc != 2) { 
    fprintf(stderr, "Usage: %s <socket>\n", argv[0]); 
    exit(-1); 
    } 

    int sock = socket(AF_UNIX, SOCK_STREAM, 0); 
    if (sock < 0) { 
    perror("socket"); 
    exit(-1); 
    } 

    struct sockaddr_un addr; 
    addr.sun_family = AF_UNIX; 
    strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1); 

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { 
    perror("Error connecting"); 
    exit(-1); 
    } 

    write(sock, msg, strlen(msg)); 
    close(sock); 

    return 0; 
} 

Der Server-Code ist der folgende:

#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/epoll.h> 
#include <sys/socket.h> 
#include <sys/un.h> 
#include <unistd.h> 

const int kMaxEvents = 100; 
const char *kSocketFile = "dummy_socket"; 

void MakeNonBlocking(int fd) { 
    int flags, s; 

    flags = fcntl (fd, F_GETFL, 0); 
    if (flags == -1) { 
    perror ("fcntl"); 
    exit(-1); 
    } 

    flags |= O_NONBLOCK; 
    s = fcntl (fd, F_SETFL, flags); 
    if (s == -1) { 
    perror ("fcntl"); 
    exit(-1); 
    } 
} 

void AcceptConnections(int sock, int epoll_fd) { 
    struct epoll_event event; 
    event.data.fd = sock; 
    event.events = EPOLLIN; 

    int insock = accept(sock, NULL, NULL); 
    if (insock < 0) { 
    perror("Error accepting connection"); 
    exit(-1); 
    } 

    MakeNonBlocking(insock); 

    int s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, insock, &event); 
    if (s < 0) { 
    perror("Epoll error adding accepted connection"); 
    exit(-1); 
    } 
    printf("Connection processed.\n"); 
} 

int main(void) { 
    int sock, efd, n; 
    struct sockaddr_un addr; 
    struct epoll_event event; 
    struct epoll_event *events; 
    char buf[1024]; 

    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { 
    perror("Error creating socket."); 
    exit(-1); 
    } 

    addr.sun_family = AF_UNIX; 
    strncpy(addr.sun_path, kSocketFile, sizeof(addr.sun_path) - 1); 
    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 
    perror("Error binding name to socket"); 
    exit(-1); 
    } 

    if (listen(sock, SOMAXCONN) < 0) { 
    perror("Error listening on socket"); 
    exit(-1); 
    } 

    MakeNonBlocking(sock); 

    if ((efd = epoll_create1(0)) < 0) { 
    perror("Epoll initialization error"); 
    exit(-1); 
    } 

    event.data.fd = sock; 
    event.events = EPOLLIN; 
    if (epoll_ctl(efd, EPOLL_CTL_ADD, sock, &event) < 0) { 
    perror("Epoll error adding socket"); 
    exit(-1); 
    } 

    events = (struct epoll_event*) calloc(kMaxEvents, sizeof(event)); 
    if (!events) { 
    perror("Error allocating event buffers"); 
    exit(-1); 
    } 

    while(1) { 
    printf("Calling epoll_wait\n"); 
    if ((n = epoll_wait(efd, events, kMaxEvents, -1)) == -1) { 
     perror("epoll_wait failure"); 
     exit(-1); 
    } 

    for (int i = 0; i < n; ++i) { 
     printf("Checking event for fd = %d\n", events[i].data.fd); 
     if (sock == events[i].data.fd) { 
     AcceptConnections(sock, efd); 
     continue; 
     } 

     int count = read(events[i].data.fd, buf, 100); 
     if (count == 0) { 
     close(events[i].data.fd); 
     } 
     write(1, buf, count); 
    } 
    } 

    free(events); 
    close(efd); 
    close(sock); 
    unlink(kSocketFile); 
    return 0; 
} 

Wenn ich den Server ausgeführt werden und eine Verbindung zum Client erhalte ich:

Calling epoll_wait 
Checking event for fd = 3 
Connection processed. 
Calling epoll_wait 
Checking event for fd = 3 
Error accepting connection: Resource temporarily unavailable 

Mit anderen Worten, ich sehe zwei Ereignisse auf dem hörenden Sockel. Irgendwelche Ideen?

+0

Ich würde gerne das überprüfen, aber ich bekomme den Laufzeitfehler: 'Fehler beim Binden des Namens an den Socket: Operation nicht erlaubt' ... – Myst

Antwort

1

zu beheben, ändern AcceptConnections zu:

void AcceptConnections(int sock, int epoll_fd) { 
    struct epoll_event event; 
    event.events = EPOLLIN; 

    int insock = accept(sock, NULL, NULL); 
    if (insock < 0) { 
    perror("Error accepting connection"); 
    exit(-1); 
    } 

    // This is the important change. 
    event.data.fd = insock; 

    MakeNonBlocking(insock); 

    int s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, insock, &event); 
    if (s < 0) { 
    perror("Epoll error adding accepted connection"); 
    exit(-1); 
    } 
    printf("Connection processed.\n"); 
} 

epoll manchmal lustig ist ...

Das Problem mit der Art und Weise ist, dass Sie das Ereignis in AcceptConnections registrieren.

epoll akzeptiert zwei verschiedenen fd Werte, einen in event.data.fd (Benutzer undurchsichtigen Datenwert) und die andere in den Anfang des epoll_ctl Funktionsaufruf (die fd das Ereignis zu steuern).

Die event.data.fd kann alles sein, und es ist die tatsächlichen Daten, die Sie in Ihrer Ereignisschleife lesen ...

... in Ihrem ursprünglichen Code, stellen Sie es auf die Buchse anstelle des Client-Horch Steckdose.

Also, wenn der Client Socket Daten hat bereit zu lesen ist, wird ein Ereignis mit event.data.fd Zeige erhöht, bei den Buchse anstelle der Client Buchse hören.

Da Sie das Ereignis (für den Client-Socket) nicht löschen, wird es wiederholt mit den von Ihnen eingestellten Daten ausgelöst (der Listening-Socket fd).

Viel Glück!