Метод, на который ссылается Beej и который упоминает AlastairG, работает примерно так:
Для каждого одновременного соединения вы поддерживаете буфер для данных, которые читаются, но еще не обработаны.(Это буфер, который Beej предлагает изменить в два раза по максимальной длине пакета).Очевидно, что буфер начинается пустым:
unsigned char recv_buffer[BUF_SIZE];
size_t recv_len = 0;
Всякий раз, когда ваш сокет доступен для чтения, считывайте оставшееся пространство в буфере, а затем сразу же пытайтесь обработать то, что у вас есть:
result = recv(sock, recv_buffer + recv_len, BUF_SIZE - recv_len, 0);
if (result > 0) {
recv_len += result;
process_buffer(recv_buffer, &recv_len);
}
process_buffer()
будет пытаться и обрабатывать данные в буфере как пакет.Если буфер еще не содержит полный пакет, он просто возвращает - в противном случае он обрабатывает данные и удаляет их из буфера.Итак, для вашего примера протокола он будет выглядеть примерно так:
void process_buffer(unsigned char *buffer, size_t *len)
{
while (*len >= 3) {
/* We have at least 3 bytes, so we have the payload length */
unsigned payload_len = buffer[2];
if (*len < 3 + payload_len) {
/* Too short - haven't recieved whole payload yet */
break;
}
/* OK - execute command */
do_command(buffer[0], buffer[1], payload_len, &buffer[3]);
/* Now shuffle the remaining data in the buffer back to the start */
*len -= 3 + payload_len;
if (*len > 0)
memmove(buffer, buffer + 3 + payload_len, *len);
}
}
(функция do_command()
проверит правильность заголовка и байта команды).
Этот вид техники заканчиваетсянеобходимо, потому что любой recv()
может вернуть короткую длину - с вашим предложенным методом, что произойдет, если длина вашей полезной нагрузки будет 500, но следующая recv()
вернет вам только 400 байтов?Вам придется сохранять эти 400 байтов до тех пор, пока сокет не станет читаемым в следующий раз.
Когда вы обрабатываете несколько одновременных клиентов, у вас просто есть один recv_buffer
и recv_len
на клиента и вставляете их вструктура для каждого клиента (которая, вероятно, содержит и другие вещи - например, сокет клиента, возможно, его адрес источника, текущее состояние и т. д.).