У меня есть серверная программа, которая использует 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