Jac*_*ack 5 c networking tcp recv
我是C的新手并编写TCP服务器,并且想知道如何从发送服务器将响应的命令的客户端处理recv().为了这个问题,我们只说头是第1个字节,命令标识符是第2个字节,有效载荷长度是第3个字节,然后是有效载荷(如果有的话).
recv()这些数据的最佳方法是什么?我想调用的recv()在第3个字节到缓冲区中读取,检查以确保标题和指令标识符有效,然后检查有效载荷长度与有效载荷长度的长度再次调用的recv(),这增加了回到前面提到的缓冲区.阅读Beej的网络文章(尤其是本节在这里:http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#sonofdataencap),然而,他建议用"足够大的数组两[最大"数据包"处理诸如获取下一个数据包之类的情况.
处理这些类型的recv()的最佳方法是什么?基本的问题,但我想高效地实现它,处理可能出现的所有情况.提前致谢.
Beej提到的方法和AlastairG提到的方法是这样的:
对于每个并发连接,您维护一个已读但尚未处理的数据的缓冲区.(这是Beej建议调整到最大数据包长度两倍的缓冲区).显然,缓冲区从空开始:
unsigned char recv_buffer[BUF_SIZE];
size_t recv_len = 0;
Run Code Online (Sandbox Code Playgroud)
只要您的套接字可读,请读入缓冲区中的剩余空间,然后立即尝试处理您拥有的内容:
result = recv(sock, recv_buffer + recv_len, BUF_SIZE - recv_len, 0);
if (result > 0) {
recv_len += result;
process_buffer(recv_buffer, &recv_len);
}
Run Code Online (Sandbox Code Playgroud)
该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);
}
}
Run Code Online (Sandbox Code Playgroud)
(该do_command()函数将检查有效的头和命令字节).
这种技术最终是必要的,因为任何一种 recv()都可以返回一个很短的长度 - 使用你提出的方法,如果你的有效载荷长度是500,会发生什么,但下一个recv()只返回400字节?你必须保存那400个字节,直到下一次套接字变得可读为止.
当你处理多个并发客户端时,你只需要一个recv_buffer和recv_len每个客户端,并将它们填充到每个客户端结构中(它可能包含其他东西 - 比如客户端的套接字,可能是它们的源地址,当前状态等).
好问题.你想要多么完美?对于所有歌唱所有舞蹈解决方案,使用异步套接字,尽可能读取所有数据,并且每当您获得新数据时,在缓冲区上调用一些数据处理功能.
这允许你做大读.如果您获得大量流水线命令,则可以在不必再次等待套接字的情况下处理它们,从而提高性能和响应时间.
在写作上做类似的事情.那就是命令处理函数写入缓冲区.如果缓冲区中有数据,则在检查套接字(select或poll)时检查可写性并尽可能多地写入,记住只删除实际从缓冲区写入的字节.
循环缓冲区在这种情况下运行良好.
有更简单的解决方案.不过这个很好.请记住,服务器可能会获得多个连接,并且可以拆分数据包.如果从套接字读入缓冲区只是为了找不到完整命令的数据,那么你对已经读过的数据做了什么?你在哪里存放?如果将它存储在与该连接相关联的缓冲区中,那么您也可以完全按照上面的描述读取缓冲区.
此解决方案还避免了为每个连接生成单独的线程 - 您可以处理任意数量的连接而不会出现任何实际问题.每个连接产生一个线程是一种不必要的系统资源浪费 - 除非在某些情况下建议使用多个线程,为此你可以让工作线程执行这样的阻塞任务,同时保持套接字处理单线程.
基本上我同意你所说的Beej所说的,但是不要一次读掉一点点的东西.一次读大块.编写像这样的套接字服务器,基于一点点套接字经验和手册页进行学习和设计,这是我参与过的最有趣的项目之一,而且非常有教育意义.
| 归档时间: |
|
| 查看次数: |
10337 次 |
| 最近记录: |