Да, обычный подход - иметь буфер «данных, которые я получил, но не обработал» для каждого клиента, достаточно большой, чтобы вместить самое большое сообщение протокола.
Вы читаете в этот буфер (всегда отслеживая, сколько данных в данный момент находится в буфере), и после каждого чтения проверяйте, есть ли у вас полное сообщение (или сообщение (я)), поскольку вы можете получить два в один раз!). Если вы это сделаете, вы обработаете сообщение, удалите его из буфера и сдвинете все оставшиеся данные до начала буфера.
Что-то примерно так:
for (i = 0; i < nclients; i++)
{
if (!FD_ISSET(client[i].fd, &read_fds))
continue;
nbytes = recv(client[i].fd, client[i].buf + client[i].bytes, sizeof(client[i].buf) - client[i].bytes, 0);
if (nbytes > 0)
{
client[i].bytes += nbytes;
while (check_for_message(client[i]))
{
size_t message_len;
message_len = process_message(client[i]);
client[i].bytes -= message_len;
memmove(client[i].buf, client[i].buf + message_len, client[i].bytes);
}
}
else
/* Handle client close or error */
}
Кстати, вы должны проверить errno == EINTR
, если select()
возвращает -1, и просто повторить цикл снова - это не фатальная ошибка.