некоторые вопросы epoll

Тема в разделе "WASM.UNIX", создана пользователем slesh, 24 июн 2010.

  1. slesh

    slesh New Member

    Публикаций:
    0
    Регистрация:
    6 фев 2009
    Сообщения:
    214
    разбираюсь сейчас с epoll. Для теста навоял простенький echo сервер
    Все вопросы по ходу кода
    Код (Text):
    1. #include <fcntl.h>
    2. #include <stdio.h>
    3. #include <unistd.h>
    4. #include <stdlib.h>
    5. #include <errno.h>
    6. #include <string.h>
    7. #include <netinet/in.h>
    8. #include <arpa/inet.h>
    9. #include <sys/epoll.h>
    10. #include <sys/types.h>
    11. #include <sys/socket.h>
    12.  
    13.  
    14. #define SERVER_ADDR INADDR_ANY
    15. #define SERVER_PORT 12345
    16. #define MAX_EVENTS  100
    17. #define EPOLL_SIZE  100
    18.  
    19.  
    20. int SetNonBlocking(int sock)
    21. {
    22.     int ret = -1;
    23.     int opts = fcntl(sock, F_GETFL);
    24.  
    25.     if (opts >= 0)
    26.     {
    27.         opts = (opts | O_NONBLOCK);
    28.         if (fcntl(sock, F_SETFL, opts) >= 0)
    29.         {
    30.             ret = 0;
    31.         }
    32.     }
    33.  
    34.     return ret;
    35. }
    36.  
    37. int main()
    38. {
    39.     int MainSock;
    40.     struct sockaddr_in saddr;
    41.     int x;
    42.     int cnt;
    43.     int len;
    44.     int epfd;
    45.     struct epoll_event ev;
    46.     struct epoll_event events[MAX_EVENTS];
    47.     char buf[256];
    48.     int Work = 1;
    49.  
    50.     MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    51.     if (MainSock != -1)
    52.     {
    53.         saddr.sin_family = AF_INET;
    54.         saddr.sin_addr.s_addr = SERVER_ADDR;
    55.         saddr.sin_port = htons(SERVER_PORT);
    56.         x = 1;
    57.         setsockopt(MainSock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x));
    58.         if (bind(MainSock, (struct sockaddr*) &saddr, sizeof (struct sockaddr_in)) != -1)
    59.         {
    60.             listen(MainSock, 100);
    61.             if ((epfd = epoll_create(EPOLL_SIZE)) != -1)  // какое желательное значение ставить для EPOLL_SIZE да и вообще какое максимально возможное
    62.             {
    63.                 ev.events = EPOLLIN;
    64.                 ev.data.fd = MainSock;
    65.                 // какое максимальное кол-во может быть добавлено сокетов?
    66.                 if (epoll_ctl(epfd, EPOLL_CTL_ADD, MainSock, &ev) != -1)
    67.                 {
    68.                     while (Work)
    69.                     {
    70.                         // может ли следующий участок вызвать ошибку при большой загруженности
    71.                         // или же он вызывает ошибку только в реально плачевном случае
    72.                         // когда уже ничего не исправишь?
    73.                         if ((cnt = epoll_wait(epfd, events, MAX_EVENTS, -1)) == -1)
    74.                         {
    75.                             Work = 0;
    76.                         }
    77.  
    78.                         for (x = 0; x < cnt; x++)
    79.                         {
    80.                             if (events[x].data.fd == MainSock) // if new client
    81.                             {
    82.                                 ev.data.fd = accept(MainSock, NULL, 0);
    83.                                 if (ev.data.fd == -1)
    84.                                 {
    85.                                     Work = 0;
    86.                                 }
    87.                                 else
    88.                                 {
    89.                                     SetNonBlocking(ev.data.fd);
    90.                                     ev.events = EPOLLIN | EPOLLET;
    91.  
    92.                                     if (epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
    93.                                     {
    94.                                         close(ev.data.fd);
    95.                                     }
    96.                                 }
    97.                             }
    98.                             else // if clients event
    99.                             {
    100.                                 if (events[x].events & EPOLLIN) // if client sended data
    101.                                 {
    102.                                     len = recv(events[x].data.fd, buf, 256, 0);
    103.                                     if (len > 0)
    104.                                     {
    105.                                         if (!strncmp(buf, "EXIT", 4))
    106.                                         {
    107.                                             send(events[x].data.fd, "CLOSED\n", 7, 0);
    108.                                             close(events[x].data.fd);
    109.                                             // надо ли тут удалять сокет из epfd (через EPOLL_CTL_ADD)?
    110.                                             // или достаточно просто его закрыть?
    111.                                         }
    112.                                         else
    113.                                             if (!strncmp(buf, "TERM", 4))
    114.                                         {
    115.                                             Work = 0;
    116.                                         }
    117.                                         else
    118.                                         {
    119.                                             send(events[x].data.fd, buf, len, 0);
    120.                                         }
    121.                                     }
    122.                                     else // client disconnected
    123.                                     {
    124.                                         close(events[x].data.fd);
    125.                                         // аналогично вышеуказанному вопросу
    126.                                     }
    127.                                 }
    128.                                 else if (events[x].events & (EPOLLHUP | EPOLLERR)) // if client error
    129.                                 {
    130.                                     close(events[x].data.fd);
    131.                                     // аналогично вышеуказанному вопросу
    132.                                 }
    133.                             }
    134.                         }
    135.                     }
    136.                 }
    137.                 close(epfd);
    138.                 // должен ли я тут закрывать все сокеты которые были добавлены ранее?
    139.                 // или закрытие epfd автоматически вызовет это??
    140.             }
    141.             close(MainSock);
    142.         }
    143.     }
    144.  
    145.     return 0;
    146. }
    И парочка вопросов дополнительных:
    1) какое наиболее оптимальное значение для MAX_EVENTS может быть, если учесть что сервер должен обрабатывать много клиентов сразу и много данных передавать
    2) echo сервер это довольно простой вариант, а если требуется более сложные вычисления которые требуют распараллеливание запросов клиентов. т.е. допустим клиент послал запрос, сервер обработал его и ответил. Какая схема многопоточности подойдет?
    Вообще в голову пришла идея чтобы как только пришли данные от клиента, так сразу запускать поток для обработки. илиже уже иметь предварительно запущенные потоки которые ожидают вызова? илиже для linux систем чуть по другому всё?
    При этом fork недопустим в данном случае из-за особенностей обработки данных.
    3) что можно еще использовать для увеличения скорости работы с сетью. Помимо TCP_NODELAY
    4) появление каких сигналов желательно обрабатывать в данном случае?
    потому как столкнулся с проблемой - если записать в сокет отключенного клиента, то появляется сигнал об это.
     
  2. slesh

    slesh New Member

    Публикаций:
    0
    Регистрация:
    6 фев 2009
    Сообщения:
    214
    P.S. ошибся в одном месте.
    Надо ли тут удалять сокет из epfd (через EPOLL_CTL_DEL)?
     
  3. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    slesh
    1) На сколько я понимаю, MAX_EVENTS должно быть равным максимальному количеству клиентов, которых ты собираешься обслуживать единовременно. Так, например, для хттп-сервера, даже с дестками тыщ посетителей в сутки, единовременная нагрузка будет исчисляться единицами.
    2) Я бы не мешал бы в кашу многопоточность (в смысле, один клиент - одни поток) с асинхронной работой epoll'a, иначе теряется весь смысл и вся прелесть данного метода.
    3)
    В своей реализации периодически натыкаюсь на ошибку non socket, поэтому, думаю что все-таки стоит, хотя во многих источниках говорится, что достаточно просто закрыть сокет.
    4)
    Оно ограничено допустимым количеством открытых дескрипторов в системе.