当前位置:  开发笔记 > 程序员 > 正文

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

如何解决《在套接字库中调用recv时,我的recv缓冲区应该有多大》经验,为你挑选了4个好方法。

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

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);

    如何确定recv_buffer的大小?我使用3000,但这是任意的.

    如果recv()收到的数据包大于我的缓冲区会怎么样?

    我怎么知道我是否收到了整个消息而没有再次调用recv并且在没有任何东西被接收时让它永远等待?

    有没有办法让缓冲区没有固定的空间,这样我可以继续添加它而不用担心空间不足?也许strcat用于连接recv()缓冲区的最新响应?

我知道这里有很多问题,但我非常感谢任何回复.



1> caf..:

这些问题的答案取决于您是使用流套接字(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()).


是的.如果没有完整的消息并将下一个`recv`中的字节填充到部分消息之后的缓冲区中,则可以通过循环来解决该问题.你不应该在`recv()`填充的原始缓冲区上使用`strstr()` - 不能保证它包含一个nul-terminator,所以它可能会导致`strstr()`崩溃.
在UDP的情况下,发送超过1400字节的UDP数据包没有任何问题.碎片是完全合法的,是IP协议的基本组成部分(即使在IPv6中,但始终初始发送方必须执行碎片).对于UDP,如果使用64 KB的缓冲区,则总是保存,因为没有IP数据包(v4或v6)可以超过64 KB(甚至在分段时也不会),这甚至包括头IIRC,因此数据将始终是肯定低于64 KB.

2> R Samuel Kla..:

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

如果有更多的数据然后你的缓冲区,它将被简单地保存在内核中以供你下次调用recv.

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

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


两种力量可以通过多种方式更有效,并且强烈建议.
详细阐述了@theatrus,一个值得注意的效率是模运算符可以被按位和掩码替换(例如x%1024 == x&1023),整数除法可以用右移操作代替(例如x/1024 = = x/2 ^ 10 == x >> 10)

3> Alex Martell..:

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

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

要动态生成缓冲区,请先将其分配mallocrealloc根据需要使用.但是这不会帮助你recv从UDP源,唉.


由于UDP始终返回最多一个UDP数据包(即使多个数据包在套接字缓冲区中)并且没有UDP数据包可以超过64 KB(IP数据包最多可能是64 KB,即使在分段时),使用64 KB缓冲区也是如此绝对安全和保证,您在UDP套接字上的recv期间永远不会丢失任何数据.

4> smokku..:

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

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

#include 
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窥视(不接收)等待消息,以获取其大小(recv返回实际大小,而不是截断的大小),并且您需要MSG_TRUNC不会溢出当前缓冲区。一旦获得大小,就分配正确的缓冲区并接收(而不是窥视,而不是截断)等待消息。
推荐阅读
惬听风吟jyy_802
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有