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

我在C中有一些关于套接字库的问题。下面是我在我的问题中提到的一段代码。

char recv_buffer[3000]; recv(socket, recv_buffer, 3000, 0); 
  1. 我如何决定recv_buffer有多大? 我正在使用3000,但它是任意的。
  2. 如果recv()收到比我的缓冲区大的数据包,会发生什么?
  3. 我怎么知道我是否收到了整个消息,而又不需要再次调用recv,并且在没有任何东西需要接收的时候能够永远等待呢?
  4. 有没有办法让缓冲区没有一个固定的空间,所以我可以继续增加它,而不用担心空间不足? 也许使用strcat将最新的recv()响应连接到缓冲区?

我知道这是一个很多的问题,但我会非常感激任何答复。

这些问题的答案因TCP / IP中是使用stream套接字( SOCK_STREAM )还是数据报套接字( SOCK_DGRAM )而异,前者对应于TCP,后者对应于UDP。

你怎么知道把缓冲区传递给recv()多大?

  • SOCK_STREAM :这并不重要。 如果你的协议是一个交易/互动的协议,只是select一个可以容纳最大的个人消息/命令,你会合理预期(3000可能是好的)的大小。 如果您的协议正在传输批量数据,那么较大的缓冲区可能会更有效率 – 一个好的经验法则与内核的接收缓冲区大小(通常约为256kB)大致相同。

  • SOCK_DGRAM :使用足够大的缓冲区来存放应用程序级协议发送的最大数据包。 如果你使用的是UDP,那么通常你的应用层协议不应该发送大于1400字节的数据包,因为它们肯定需要被分割和重新组装。

如果recv获得大于缓冲区的数据包会发生什么情况?

  • SOCK_STREAM :这个问题并没有什么意义,因为stream套接字没有包的概念 – 它们只是连续的字节stream。 如果可用的字节数多于缓冲区的空间,那么它们将被操作系统排队等待下次调用recv

  • SOCK_DGRAM :超出的字节被丢弃。

我怎么知道我是否收到了整个消息?

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

  • SOCK_DGRAM :单个recv调用总是返回一个数据报。

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

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

对于TCP等stream媒体协议,几乎可以将缓冲区设置为任意大小。 也就是说,推荐使用2的幂数(例如4096或8192)。

如果有更多的数据,那么你的缓冲区,它将被简单地保存在你的下一个调用recv的内核。

是的,你可以继续增长你的缓冲区。 你可以在偏移量idx处开始缓冲区的中间,你可以这样做:

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

如果你有一个SOCK_STREAM套接字,那么recv就会从stream中获得“最多3000字节”。 对缓冲区的大小没有明确的指导意见:唯一一次你知道stream是多大的时候,是什么时候完成的;-)。

如果你有一个SOCK_DGRAM套接字,并且数据报大于缓冲区,则recv用数据报的第一部分填充缓冲区,返回-1,并将errno设置为EMSGSIZE。 不幸的是,如果协议是UDP,这意味着数据报的其余部分丢失 – 为什么UDP被称为不可靠协议的一部分(我知道有可靠的数据报协议,但它们不是很受欢迎 – 我不能在TCP / IP家族中名列前茅,尽pipe知道后者相当不错;-)。

要dynamic增长一个缓冲区,最初使用malloc分配,并根据需要使用realloc 。 但是,这不会帮助你从UDP源recv ,唉。

对于你的问题没有绝对的答案,因为技术总是与实现相关的。 我假设你在UDP通信,因为传入的缓冲区大小不会给TCP通信带来问题。

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

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

什么是最大吞吐量的UDP数据包的最佳大小?
互联网上最大的安全UDP数据包大小是多less?

对于SOCK_STREAM套接字来说,缓冲区的大小并不重要,因为你只需要拖动一些等待的字节,并且可以在下次调用时检索更多的字节。 只要select你能承受的任何缓冲区大小。

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

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

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

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

然后,你可以只是malloc()缓冲区和检索数据报。

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