是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
,并将它们填充到每个客户端结构中(这可能也包含其他内容 - 如客户端套接字,可能是它们的源地址,当前状态等等。)。
来源
2010-12-03 01:29:52
caf
谢谢AlastairG。我基本上试图保持它尽可能简单,只处理最常见的情况,如读入下一个数据包,或读取部分数据包。我实际上使用select来监视传入数据的客户端套接字(在这种情况下是命令),所以我不认为这是异步套接字的问题。我的方法是读取3个字节,然后有效载荷不起作用?我不想实现Beej所说的原因是因为如果没有实际的代码/伪代码就很难理解。感谢您的建议:) – Jack 2010-12-02 16:14:26
对于处理传入数据后的写入/发送,我只是简单地使用Beej的sendall方法的变体,因为我知道数据包的长度..显示在这里:http:// beej页面没有自动跳转/导向/ bgnet /输出/ HTML /多页/ advanced.html#sendall。我更关心阅读。 – Jack 2010-12-02 16:15:52
我对写作的评论只是为了完整 - 以防其他人想知道如何编写一个非常好的套接字服务器。我要说的一件事是,我已经尝试编写小而简单的套接字服务器,但你很快发现你只需要按照我描述的方式来完成它。但并非总是如此。读取3个字节可以工作,但为什么要这样做? – AlastairG 2010-12-02 16:33:14