在套接字库中调用recv时,我的recv缓冲区应该有多大

时间:2010-05-19 00:19:47

标签: c sockets buffer recv

我对C中的套接字库有几个问题。这是我在问题中引用的一段代码。

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. 我如何决定制作recv_buffer有多大?我使用3000,但这是任意的。
  2. 如果recv()收到的数据包大于我的缓冲区,会发生什么?
  3. 我怎么知道我是否收到了整个邮件而没有再次调用recv,并且在没有任何东西可以收到的时候让它永远等待?
  4. 有没有办法可以让缓冲区没有固定的空间,这样我可以继续添加它而不用担心空间不足?也许使用strcat将最新的recv()响应连接到缓冲区?
  5. 我知道其中有很多问题,但我非常感谢任何回复。

6 个答案:

答案 0 :(得分:217)

这些问题的答案取决于您是使用流套接字(SOCK_STREAM)还是数据报套接字(SOCK_DGRAM) - 在TCP / IP中,前者对应TCP而后者对应TCP到UDP。

您如何知道将缓冲区传递给recv()有多大?

  • SOCK_STREAM:这并不重要。如果您的协议是事务性/交互式协议,只需选择一个可以容纳您合理预期的最大单个消息/命令的大小(3000可能没问题)。如果您的协议正在传输批量数据,那么更大的缓冲区可以更有效 - 一个好的经验法则与套接字的内核接收缓冲区大小(通常大约256kB)大致相同。

  • SOCK_DGRAM:使用足够大的缓冲区来容纳应用程序级协议发送的最大数据包。如果您正在使用UDP,那么通常您的应用程序级协议不应该发送大于大约1400字节的数据包,因为它们肯定需要进行分段和重新组装。

如果recv获取的数据包大于缓冲区,会发生什么?

  • SOCK_STREAM:这个问题并不像put那样有意义,因为流套接字没有数据包的概念 - 它们只是一个连续的字节流。如果有更多字节可供阅读,而缓冲区有空间,那么它们将被操作系统排队,可用于下次调用recv

  • SOCK_DGRAM:丢弃多余的字节。

我怎么知道我是否收到了整条信息?

  • SOCK_STREAM:您需要构建一些方法来确定应用程序级协议中的消息结束。通常,这是一个长度前缀(以消息的长度开始每个消息)或消息结束分隔符(例如,它可能只是基于文本的协议中的换行符)。第三种较少使用的选项是为每条消息强制使用固定大小。这些选项的组合也是可能的 - 例如,包含长度值的固定大小的标题。

  • SOCK_DGRAM:单个recv调用始终返回单个数据报。

有没有办法可以让缓冲区没有固定的空间,这样我可以继续添加它而不用担心空间不足?

没有。但是,您可以尝试使用realloc()调整缓冲区的大小(如果最初使用malloc()calloc()分配,则确实如此)。

答案 1 :(得分:15)

对于TCP等流媒体协议,您几乎可以将缓冲区设置为任意大小。也就是说,建议使用常用值为2的幂,如4096或8192。

如果有更多数据,那么您的缓冲区将被保存在内核中,以便您下次调用recv

是的,你可以继续增加你的缓冲区。您可以从偏移idx开始在缓冲区中间执行recv,您可以这样做:

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);

答案 2 :(得分:15)

如果你有一个SOCK_STREAM套接字,recv只能从流中获得“前3000字节”。关于缓冲区的大小没有明确的指导:只有当你知道一个流有多大时,它才会完成; - )。

如果你有一个SOCK_DGRAM套接字,并且数据报大于缓冲区,recv用数据报的第一部分填充缓冲区,返回-1,并将errno设置为EMSGSIZE。不幸的是,如果协议是UDP,这意味着数据报的其余部分丢失 - 为什么UDP被称为不可靠协议的部分原因(我知道有可靠的数据报协议,但它们不是很流行 - 我不能在TCP / IP系列中命名一个,尽管很好地了解后者; - )。

要动态增长缓冲区,最初使用malloc进行分配,并根据需要使用realloc。但是这对于来自UDP源的recv无济于事,唉。

答案 3 :(得分:4)

对于SOCK_STREAM套接字,缓冲区大小并不重要,因为您只是拉动了一些等待的字节,您可以在下一次调用中检索更多。只需选择您能承受的任何缓冲区大小。

对于SOCK_DGRAM套接字,您将获得等待消息的拟合部分,其余部分将被丢弃。您可以使用以下ioctl获取等待数据报大小:

#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);

或者,您可以使用MSG_PEEK调用的MSG_TRUNCrecv()标志来获取等待的数据报大小。

ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);

你需要MSG_PEEK来查看(不接收)等待的消息 - recv返回真实的,而不是截断的大小;并且您需要MSG_TRUNC不要溢出当前缓冲区。

然后你可以malloc(size)真正的缓冲区和recv()数据报。

答案 4 :(得分:1)

您的问题没有绝对的答案,因为技术始终必然是特定于实现的。我假设您正在使用UDP进行通信,因为传入的缓冲区大小不会给TCP通信带来问题。

根据RFC 768,UDP的数据包大小(包含头部)可以是8到65 515字节。因此,传入缓冲区的防故障大小为65 507字节(~64KB)

但是,并非所有大型数据包都可以通过网络设备正确路由,请参阅现有讨论以获取更多信息:

What is the optimal size of a UDP packet for maximum throughput?
What is the largest Safe UDP Packet Size on the Internet

答案 5 :(得分:-4)

16kb是对的;如果你使用的是千兆以太网,每个数据包的大小可能是9kb。