Почему самописный сервер на си работает медленнее nginx?

Вроде бы должно работать намного быстрее, но на практике получаются лютые тормоза.

#include <sys/epoll.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stddef.h>
#include <unistd.h>
#define COUNT_EVENTS 10

int main() {
    int sfd = socket( AF_INET, SOCK_STREAM, 0 );
    fcntl( sfd, F_SETFL, O_NONBLOCK );

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons( 5002 );
    addr.sin_addr.s_addr = htonl( INADDR_ANY );
    bind( sfd, (struct sockaddr *)&addr, sizeof( addr ) );

    listen( sfd, 1 );

    int ep = epoll_create1( 0 );
    struct epoll_event server_event;
    struct epoll_event events[COUNT_EVENTS];
    server_event.events = EPOLLIN | EPOLLET;
    server_event.data.fd = sfd;

    epoll_ctl( ep, EPOLL_CTL_ADD, sfd, &server_event );

    while( 1 ) {
        int count_events = epoll_wait( ep, events, COUNT_EVENTS, -1 );

        for( int i = 0; i < count_events; i++ ) {
            int fd = events[i].data.fd;

            if( events[i].events & EPOLLRDHUP ) {
                close( fd );
                epoll_ctl( ep, EPOLL_CTL_DEL, fd, NULL );
            } else if( events[i].events & EPOLLIN ) {
                if( fd == sfd ) {
                    int cfd = accept(
                        sfd,
                        NULL,
                        NULL
                    );
                    fcntl( cfd, F_SETFL, O_NONBLOCK );

                    struct epoll_event ev = { 0 };
                    ev.events = EPOLLRDHUP | EPOLLET;
                    ev.data.fd = cfd;
                    epoll_ctl( ep, EPOLL_CTL_ADD, cfd, &ev );

                }
            }
        }
    }
}

Ответы (2 шт):

Автор решения: Fat-Zer

Как заметил @eri, основная проблема в том, что размер очереди ожидающих соединений сокета всего 1, т.е. :

listen( sfd, 1 );

должно быть*:

listen( sfd, 511 );

Что вызывает наблюдаемое поведение

Подразумевая, что клиент делает что-то примитивное вроде:

for(int i=0; i<N; i++){
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    connect(sfd,addr,addrlen);
    close(sfd);
}

то иногда возникает ситуация, когда клиент успевает создать новый сокет и отправить SYN-запрос для установки соединения до того как сервер успеет обработать предыдущий accept()'ом. В результате чего очередь приёма переполняется и этот запрос отбрасывается, а клиент ожидает 1 секунду перед его повторной отправкой.


* 511 — значение взятое из strace'а nginx'а с кофигом по умолчанию.

→ Ссылка
Автор решения: user17283789

Как я разобрался проблема в следующем: прослушивающий входящие соединение сокет создан с флагами EPOLLIN | EPOLLET, epollet генерит событие один раз (edge-triggered), даже если пришло несколько соединений. Accept забирает первое соединение и цикл продолжается, при этом накапливается очередь из неподключенных коннектов, идет переполнение и амба. Выход - либо не юзать EPOLLET на главном сокете, тогда событие будет генерится при каждой попытке коннекта, либо циклом вызывать accept пока не вернется -1

→ Ссылка