Epoll не до конца читает данные из сокета

178
01 декабря 2021, 03:00
while(true)
{
    int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds == -1) 
        std::cout << "[ERROR] nfds error" << std::endl;

    for (size_t k = 0; k < nfds; ++k)
    {
        if(events[k].data.fd == sock)
        {            
            listen_res = listen(events[k].data.fd, 1);
            if(listen_res = 1)
                    {
                connection = accept(sock, (struct sockaddr*)&from, &len); 
                if(connection == -1)
                    continue;
                if (setnonblocking(connection) == -1)
                    std::cout << "[ERROR] setnonblocking()" << std::endl;

                ev.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLET;
                ev.data.fd = connection;

                if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connection, &ev) == -1)
                                std::cout << "[ERROR] epoll_ctl(connection) returned an error " << std::endl;
                else
                    std::cout << "[INFO] New Client's IP: " << inet_ntoa(from.sin_addr) << std::endl;

            }
        }
        else
        {

            if (events[k].events & (EPOLLRDHUP | EPOLLHUP))
            {
                std::cout << "Disconnect" << std::endl;
                ThreadPool.add(task);
                close(events[k].data.fd);
                epoll_ctl(epollfd, EPOLL_CTL_DEL, events[k].data.fd, &ev);
            }
            else if (events[k].events & EPOLLIN)
            {

                do
                {
                    read(events[k].data.fd, &Packet, sizeof(PacketStructure));
                    do_work(&Packet);
                }while(errno != EAGAIN);

                }

            }



    }



}

После определенного количества посланных пакетов (отправляю части файла размером по 128 байт) этот код перестает принимать пакеты от клиента. Как можно решить проблему?

Answer 1
do {
  read(events[k].data.fd, &Packet, sizeof(PacketStructure));
  do_work(&Packet);
} while(errno != EAGAIN);

Скорей всего проблема в том, что перед вызовом read() для такой проверки errno нужно обнулять вручную т.к. read() как и ни один другой системный вызов этого не делает. В итоге все исполнения цикла кроме первого обрабатывают ровно один пакет, а когда возникает ситуация, что epoll_wait() возвращает сокет в котором готово два пакета на считывание, при ещё одном вызове epoll_wait() всё виснет.

Можно также изменить условие на что-то вроде:

 ssize_t read_rv;
 do {
   read_rv = read(events[k].data.fd, &Packet, sizeof(PacketStructure));
   do_work(&Packet);
 } while(read_rv >= 0 && errno != EAGAIN);
 errno=0;

Также из плохого/подозрительного в приведённом куске:

  • У read'а должен быть контроль ошибок т.к. в противном случае вероятен вечный цикл.
  • Вообще говоря, read() из tcp-сокета может корректно вернуть любое количество данных (от одного байта до sizeof(PacketStructure)). Соответственно при обработке пакета это надо учитывать...
  • Передавать sizeof(PacketStructure) read'у не самая лучшая идея т.к. размер многих типов данных архитектурно зависимый... Не говоря уже о порядке байт.
  • Как говорит тётя маня EPOLLERR, в epoll_ctl явно передавать не обязательно. А вот обрабатывать — желательно.
READ ALSO
Проблема с LU-разложением С++

Проблема с LU-разложением С++

Я написал код в соответствии с алгоритмом, но результат неверенСогласно алгоритму, мы должны указать размер матрицы и вручную заполнить...

302
Как запускать gui приложение Qt без консоли?

Как запускать gui приложение Qt без консоли?

Проблема такова : исполняемый файл готового приложения запускается вместе с консолью позадиПараметр "Run in terminal" снят, приложение создано...

113
Клонирование класса GameObject Unity

Клонирование класса GameObject Unity

Я хочу клонировать в скрипте GameObject без его появления на сцене(так что Instantiate не подойдёт)

157
Аналог (подобие) include в C#

Аналог (подобие) include в C#

(Просьба не удалять вопросЧетко сформулированного нет

74