Понимание kqueue в TCP

Я следую руководствам по kqueue (в частности, http://eradman.com/posts/kqueue-tcp.html и https://wiki.netbsd.org/tutorials/kqueue_tutorial/), и есть части, которые я не понимаю. Вот мой (отредактированный) код:

// assume internal_socket is listening
void run_server(int internal_socket) {
    const int nchanges = 1;
    const int nevents = BACKLOG;

    struct kevent change_list[nchanges];
    struct kevent event_list[nevents];

    int kq = kqueue();

    if (kq == -1) {
        // error
    }

    EV_SET(&change_list, sock_internal, EVFILT_READ, EV_ADD, 0, 0, 0);

    while (true) {
        int nev = kevent(kq, change_list, nchanges, event_list, nevents, NULL);

        if (nev == -1) {
            // error
        }

        for (int i = 0; i < nev; ++i) {
            if (event_list[i].flags & EV_EOF) {
                int fd = event_list[i].ident;
                EV_SET(&change_list, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
                if (kevent(kq, &change_list, nchanges, NULL, 0, NULL) == -1) {
                    // error
                }
                close(fd);
            } else if (event_list[i].ident == sock_internal) {
                int fd = accept(event_list[i].ident, ...);
                // do stuff
            } else if (event_list[i].flags == EVFILT_READ) {
                int bytes_read = recv(event_list[i].ident, ...);
                // do stuff
            }
        } // for
    } // while (true)
} // func

я не понимаю:

  1. Правильно ли я установил nevents = BACKLOG, т.е. количество одновременных подключений? Если нет, то какими должны быть события?

  2. Почему я проверяю event_list[i].flags & EV_EOF? Мое лучшее предположение: если соединение не удалось, пока сокет находился в очереди, я хочу удалить этот сокет из очереди? Но почему я снова звоню Кевенту?

  3. В том же разделе, что и предыдущий пункт, я вызываю close(fd). Это правильно? В учебнике по эрадману есть дополнительное колдовство, но я не понимаю, почему.

  4. Если я правильно понимаю, kqueue может вернуться, когда я буду готов прочитать частичное сообщение. Как я узнаю, что сообщение готово?

Если это актуально, я работаю над OS X.


person sudo rm -rf slash    schedule 02.05.2017    source источник


Ответы (1)


Быстрые мысли о коде/вопросах:

  1. No.

    Нет требования, чтобы BACKLOG == nevents.

    Вы можете выбирать события из очереди по одному или по десятку за раз, это в основном зависит от ваших предпочтений и минимизации системных вызовов по сравнению с памятью/стеком.

    Также не очень часто у вас будут все соединения, одновременно запускающие события... нет реального смысла тратить столько памяти, особенно если учесть большой параллелизм, это может означать, что большой объем памяти может привести к промахам кеша и, возможно, привести к снижению производительности.

  2. #P4#
    #P5#
    #P6# #P7# #P8# #P9#
  3. вызов close разумен... на самом деле зависит от того, что вы хотите. Я мог бы сохранить соединение только для чтения данных. Или, возможно, вызовите shutdown, так как это считается более вежливым (но наверное, в наше время это не так уж и важно).

  4. Вы не знаете. Это не проблема TCP/IP.

    Обтекание сообщений должно выполняться реализуемым протоколом (например, Websockets/HTTP). Каждый протокол имеет различный дизайн упаковки/завершения сообщений.

    Уровень TCP/IP упаковывает пакеты. В дикой природе они часто ограничены 1500 байтами, а многие части Интернета работают на 576 байтах. Вы можете использовать Google MTU для получения дополнительной информации.

Примечание:

  • Возможно, вы захотите добавить новых клиентов в группу kqueue.

  • Я бы рассмотрел возможность сброса значения nchanges каждый цикл.

person Myst    schedule 03.05.2017