2016-06-19 2 views
1

Я впервые играю с epoll в Linux и вижу странное поведение. Specificall, когда я подключаюсь к клиенту к сокету, я вижу два события, выпущенных epoll_wait на стороне сервера. Когда я вызываю accept во второй попытке, я получаю сообщение «временно недоступный».Видя два события принятия на epoll

Вот простой код клиента:

#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; 
} 

Серверный код является следующее:

#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; 
} 

Когда я запускать сервер и подключиться к клиенту, я получаю:

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

Другими словами, я вижу два события в гнезде для прослушивания. Есть идеи?

+0

Мне бы хотелось это проверить, но я получаю ошибку времени выполнения: 'Ошибка привязки имени к сокету: операция не разрешена' ... – Myst

ответ

1

Чтобы исправить, изменить AcceptConnections на:

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 иногда смешно ...

Проблема заключается в том, как вы зарегистрировать событие в AcceptConnections.

epoll принимает два различных fd значения, один в event.data.fd (пользователя непрозрачного значения данных), а другой в начале вызова функции в epoll_ctl (на fd контрольный случай).

event.data.fd может быть что угодно, и это фактические данные, которые вы читали в цикле событий ...

... в исходном коде, вы установите его на прослушивания гнездо вместо клиента гнездо.

Таким образом, когда клиент гнезда имеет данные готовы для чтения, событие поднимается с event.data.fd указывающих на прослушивания гнезда вместо клиента гнезда.

Поскольку вы не очищаете событие (для клиентского сокета), оно многократно увеличивается с данными, которые вы установили (прослушивающий сокет fd).

Удачи!

Смежные вопросы