147

我对 C 中的套接字库有一些疑问。这是我将在我的问题中引用的代码片段。

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. 我如何决定做多大的recv_buffer?我使用的是 3000,但它是任意的。
  2. 如果recv()收到比我的缓冲区大的数据包会发生什么?
  3. 我怎么知道我是否已经收到了整个消息而无需再次调用 recv 并在没有收到任何内容时让它永远等待?
  4. 有没有办法让缓冲区没有固定数量的空间,这样我就可以继续添加它而不必担心空间不足?也许使用strcat连接recv()到缓冲区的最新响应?

我知道这是一个很多问题,但我将非常感谢任何回应。

4

6 回答 6

249

这些问题的答案取决于您使用的是流套接字 ( 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()or分配calloc()的)来调整缓冲区的大小。

于 2010-05-19T00:53:15.840 回答
18

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

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

是的,您可以继续增加缓冲区。您可以在从 offset 开始的缓冲区中间执行 recv idx,您可以:

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
于 2010-05-19T00:28:03.377 回答
15

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

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

要动态增长缓冲区,最初分配它mallocrealloc根据需要使用。但这不会帮助你recv从 UDP 源,唉。

于 2010-05-19T00:28:05.910 回答
9

对于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()数据报。

于 2016-08-09T18:32:02.543 回答
1

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

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

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

对于最大吞吐量,UDP 数据包的最佳大小是多少?
Internet 上最大的安全 UDP 数据包大小是多少

于 2010-05-19T01:22:18.160 回答
-4

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

于 2010-05-19T00:46:48.960 回答