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


129

我对C语言中的套接字库有一些疑问。这是我在问题中将参考的一小段代码。

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. 我如何确定要使recv_buffer多大?我正在使用3000,但是它是任意的。
  2. 如果recv()收到比我的缓冲区大的数据包怎么办?
  3. 我怎么知道我是否收到了整个消息而又没有再次调用recv,并且在什么都没收到的情况下让它永远等待?
  4. 有没有一种方法可以使缓冲区没有固定的空间,这样我就可以继续添加缓冲区而不必担心空间不足?也许strcat用于连接recv()对缓冲区的最新响应?

我知道很多问题合而为一,但是我将不胜感激。

Answers:


230

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

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

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

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

如果recv数据包大于缓冲区会怎样?

  • SOCK_STREAM:这个问题实际上并没有什么意义,因为流套接字没有数据包的概念-它们只是连续的字节流。如果要读取的字节多于缓冲区可容纳的空间,那么操作系统会将它们排队,并可供您下次调用recv

  • SOCK_DGRAM:多余的字节将被丢弃。

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

  • SOCK_STREAM:您需要构建确定消息终止到应用程序级协议的某种方式。通常,这可以是长度前缀(以消息的长度开始每个消息)或消息结尾定界符(例如,在基于文本的协议中可能只是换行符)。第三种较少使用的选项是为每个消息强制设置固定大小。这些选项的组合也是可能的-例如,包含长度值的固定大小的标头。

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

有没有一种方法可以使缓冲区没有固定的空间量,以便可以继续添加缓冲区而不必担心空间不足?

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


1
我使用的协议中的消息末尾有一个“ / r / n / r / n”。我有一个do while循环,在内部调用recv,然后将消息放在recv_buffer的开头。而我的while语句看起来像while((!(strstr(recv_buffer,“ \ r \ n \ r \ n”)));我的问题是,一个recv是否有可能获得“ \ r \ n”并下一个Recv获得“ \ r \ n”,以便我的while条件永远不会成真吗?
adhanlon 2010年

3
是的。您可以通过遍历是否没有完整的消息并recv在部分消息之后将下一个字节填充到缓冲区中来解决该问题。您不应该strstr()在由填充的原始缓冲区上使用recv()-无法保证它包含nul终止符,因此它可能导致strstr()崩溃。
caf 2010年

3
如果是UDP,则发送1400字节以上的UDP数据包没有任何问题。分片是完全合法的,并且是IP协议的基本组成部分(即使在IPv6中,但始终有初始发送者必须执行分片)。对于UDP,如果使用64 KB的缓冲区,则始终会保存,因为IP数据包(v4或v6)的大小都不能超过64 KB(即使是分段的也不是),并且甚至包括标头IIRC,因此数据始终为肯定低于64 KB。
Mecki 2012年

1
@caf是否需要在每次调用recv()时清空缓冲区?我看过代码循环并收集数据,然后再次循环应收集更多数据。但是,如果缓冲区变满了,您是否需要清空它,以避免由于写入过程而分配给缓冲区的内存量导​​致内存冲突?
Alex_Nabu 2015年

1
@Alex_Nabu:只要有剩余空间,就不需要将其清空,并且您也不会告诉recv()写入的字节数要多于剩余空间。
caf 2015年

16

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

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

是的,您可以继续增加缓冲区。您可以从offset开始对缓冲区的中间进行recv idx,您可以这样做:

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

6
两个的幂可以以多种方式更有效,并且强烈建议使用。
Yann Ramin 2010年

3
在@theatrus上进行详细说明,一个显着的效率是可以将模运算符替换为按位和掩码(例如x%1024 == x&1023),并且可以将整数除以右移运算(例如x / 1024 = = x / 2 ^ 10 == x >> 10)
vicatcu 2010年

15

如果您有SOCK_STREAM套接字,则recv只需从流中获取“最多前3000个字节”。对于缓冲区的大小没有明确的指导:只有知道流的大小时,才是全部完成;-)。

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

要动态增加缓冲区,请先分配缓冲区,malloc然后realloc根据需要使用。但这对recvUDP源无济于事。


7
由于UDP始终最多返回一个UDP数据包(即使套接字缓冲区中有多个),并且UDP数据包不能超过64 KB(即使是分段的IP数据包也最多为64 KB),因此使用64 KB缓冲区绝对安全,并保证您在UDP套接字上的接收期间不会丢失任何数据。
Mecki 2012年

7

对于SOCK_STREAM套接字,缓冲区大小并不重要,因为您只是拉出一些等待的字节,并且可以在下一个调用中检索更多字节。只需选择您可以负担的任何缓冲区大小即可。

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

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

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

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

您需要MSG_PEEK窥视(不接收)等待消息-recv返回实际的大小,而不是截断的大小;而且您不必MSG_TRUNC溢出当前缓冲区。

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


MSG_PEEK | MSG_TRUNC没有任何意义。
罗恩侯爵

3
您希望MSG_PEEK窥视(不接收)等待消息,以获取其大小(recv返回实际大小,而不是截断的大小),并且您需要MSG_TRUNC不会溢出当前缓冲区。一旦获得大小,就分配正确的缓冲区并接收(而不是窥视,而不是截断)等待消息。
smokku

@Alex Martelli说64KB是UDP数据包的最大大小,因此,如果我们malloc()要使用64KB的缓冲区,那MSG_TRUNC是不必要的吗?
mLstudent33

1
IP协议支持分段,因此数据报可能大于单个数据包-将被分段并在多个数据包中传输。也SOCK_DGRAM不仅是UDP。
smokku

1

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

根据RFC 768,UDP的数据包大小(包括报头)范围为8到65 515字节。因此,传入缓冲区的防故障大小为65507字节(〜64KB)

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

为了获得最大吞吐量,UDP数据包的最佳大小是多少?
互联网上最大的安全UDP数据包大小是多少


-4

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


3
TCP套接字是流,这意味着recv可能返回从多个数据包累积的数据,因此数据包的大小与TCP完全无关。如果是UDP,则每个recv调用最多返回单个UDP数据包,此处的数据包大小是相关的,但正确的数据包大小约为64 KB,因为如果需要,UDP数据包可能会(并且经常会)分段。但是,任何IP数据包都不会超过64 KB,即使有碎片也不会超过64 KB,因此UDP套接字上的recv最多只能返回64 KB(对于当前数据包,未返回的内容将被丢弃!)
Mecki 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.