Почему select не сигнализирует дескриптор файла с буферизованными данными? - PullRequest
0 голосов
/ 04 февраля 2019

У меня есть серверная программа, которая использует select с 10-секундным таймаутом для ожидания активности на нескольких неблокирующих клиентских подключениях.Каждый раз, когда select сигнализирует о входном сигнале для чтения, сервер считывает и обрабатывает до 1 КБ данных.Если есть ответ для отправки, он отправит его.Затем он вернется к select.

fcntl(clientFd, F_SETFL, fcntl(clientFd, F_GETFL, 0) | O_NONBLOCK);
while (true) {
  FD_ZERO(&readfds);
  FD_SET(clientFd, &readfds);
  timeout = (struct timeval){.tv_sec = 10};
  select(clientFd + 1, &readfds, NULL, NULL, &timeout);
  if (FD_ISSET(clientFd, &readfds)) {
    uint8_t recv_buffer[1024];
    fread(recv_buffer, 1, 1024, clientFile);
    // process & maybe respond / fflush
  }
}

Диапазон сообщений клиента варьируется от маленьких (10 или 100 байт) сообщений до больших (сообщения 3-10 КБ).Клиенты будут ждать ответа до 30 секунд, прежде чем повесить трубку.Когда они вешают трубку, они отправляют небольшое 10-байтовое сообщение о зависании.

Разыгрывается сценарий, который нарушает мое понимание того, как select должен работать.Клиент отправляет сообщение, которое меньше буфера чтения, поэтому сервер считывает все и отвечает.Затем клиент отправляет сообщение 3K.Сервер считывает первые 1 КБ, а затем следующие select тайм-ауты вызовов.Я ожидал, что select немедленно вернется и сообщит, что данные доступны, если в ядре были буферизованы данные для этого файлового дескриптора.После истечения времени ожидания клиент отправляет сообщение о зависании.Когда приходит сообщение о зависании, внезапно сервер может select этот дескриптор файла, и он читает второй и третий фрагмент большего сообщения клиента, за которым следует меньшее сообщение клиента.

Я оченьопределенная синхронизация этих событий из-за (1) задействованных длительных тайм-аутов, (2) tcpdump диалога, подтверждающего, что сообщение 3K поступило как один TCP-сегмент.

Простая демонстрационная программа с использованием pipe не демонстрирует такого поведения, как и другая простая демонстрация с использованием сокета TCP.Поэтому я должен делать что-то глупое в серверной программе.Что я должен проверять?

Я проверяю, что:

  • FD чтения клиента находится в наборе чтения FD до выбора
  • Read FD клиента находится в наборе read FD после выбора (это не так)
  • Размер прочитанных данных (всегда 1K, если доступно хотя бы столько)
  • Второй вызовfread будет блокироваться (это не так. Я попытался в отладчике, и я также сделал другой прогон с размером recv_buffer до 4K, который прочитал все сообщение 3K)
  • Фрагментация пакета (нет)через tcpdump

wsd

1 Ответ

0 голосов
/ 04 февраля 2019

Размещение того, что сказал @nm.Отключится, если они оставят свой комментарий в качестве ответа:

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

...