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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2862071/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:28:28  来源:igfitidea点击:

How large should my recv buffer be when calling recv in the socket library

csocketsbufferrecv

提问by adhanlon

I have a few questions about the socket library in C. Here is a snippet of code I'll refer to in my questions.

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

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. How do I decide how big to make recv_buffer? I'm using 3000, but it's arbitrary.
  2. what happens if recv()receives a packet bigger than my buffer?
  3. how can I know if I have received the entire message without calling recv again and have it wait forever when there is nothing to be received?
  4. is there a way I can make a buffer not have a fixed amount of space, so that I can keep adding to it without fear of running out of space? maybe using strcatto concatenate the latest recv()response to the buffer?
  1. 我如何决定使 recv_buffer 有多大?我正在使用 3000,但它是任意的。
  2. 如果recv()收到比我的缓冲区大的数据包会发生什么?
  3. 我怎么知道我是否在没有再次调用 recv 的情况下收到了整条消息,并且在没有收到任何消息时让它永远等待?
  4. 有没有办法让缓冲区没有固定的空间量,这样我就可以继续添加它而不必担心空间不足?也许strcat用于将最新recv()响应连接到缓冲区?

I know it's a lot of questions in one, but I would greatly appreciate any responses.

我知道其中有很多问题,但我将不胜感激任何答复。

回答by caf

The answers to these questions vary depending on whether you are using a stream socket (SOCK_STREAM) or a datagram socket (SOCK_DGRAM) - within TCP/IP, the former corresponds to TCP and the latter to UDP.

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

How do you know how big to make the buffer passed to recv()?

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

  • SOCK_STREAM: It doesn't really matter too much. If your protocol is a transactional / interactive one just pick a size that can hold the largest individual message / command you would reasonably expect (3000 is likely fine). If your protocol is transferring bulk data, then larger buffers can be more efficient - a good rule of thumb is around the same as the kernel receive buffer size of the socket (often something around 256kB).

  • SOCK_DGRAM: Use a buffer large enough to hold the biggest packet that your application-level protocol ever sends. If you're using UDP, then in general your application-level protocol shouldn't be sending packets larger than about 1400 bytes, because they'll certainly need to be fragmented and reassembled.

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

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

What happens if recvgets a packet larger than the buffer?

如果recv得到一个大于缓冲区的数据包会发生什么?

  • SOCK_STREAM: The question doesn't really make sense as put, because stream sockets don't have a concept of packets - they're just a continuous stream of bytes. If there's more bytes available to read than your buffer has room for, then they'll be queued by the OS and available for your next call to recv.

  • SOCK_DGRAM: The excess bytes are discarded.

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

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

How can I know if I have received the entire message?

我怎么知道我是否收到了完整的邮件?

  • SOCK_STREAM: You need to build some way of determining the end-of-message into your application-level protocol. Commonly this is either a length prefix (starting each message with the length of the message) or an end-of-message delimiter (which might just be a newline in a text-based protocol, for example). A third, lesser-used, option is to mandate a fixed size for each message. Combinations of these options are also possible - for example, a fixed-size header that includes a length value.

  • SOCK_DGRAM: An single recvcall always returns a single datagram.

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

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

Is there a way I can make a buffer not have a fixed amount of space, so that I can keep adding to it without fear of running out of space?

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

No. However, you can try to resize the buffer using realloc()(if it was originally allocated with malloc()or calloc(), that is).

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

回答by R Samuel Klatchko

For streaming protocols such as TCP, you can pretty much set your buffer to any size. That said, common values that are powers of 2 such as 4096 or 8192 are recommended.

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

If there is more data then what your buffer, it will simply be saved in the kernel for your next call to recv.

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

Yes, you can keep growing your buffer. You can do a recv into the middle of the buffer starting at offset idx, you would do:

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

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

回答by Alex Martelli

If you have a SOCK_STREAMsocket, recvjust gets "up to the first 3000 bytes" from the stream. There is no clear guidance on how big to make the buffer: the only time you know how big a stream is, is when it's all done;-).

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

If you have a SOCK_DGRAMsocket, and the datagram is larger than the buffer, recvfills the buffer with the first part of the datagram, returns -1, and sets errno to EMSGSIZE. Unfortunately, if the protocol is UDP, this means the rest of the datagram is lost -- part of why UDP is called an unreliableprotocol (I know that there are reliable datagram protocols but they aren't very popular -- I couldn't name one in the TCP/IP family, despite knowing the latter pretty well;-).

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

To grow a buffer dynamically, allocate it initially with mallocand use reallocas needed. But that won't help you with recvfrom a UDP source, alas.

要动态增长缓冲区,请根据需要对其进行初始分配malloc和使用realloc。但这对recv来自 UDP 源的您没有帮助,唉。

回答by smokku

For SOCK_STREAMsocket, the buffer size does not really matter, because you are just pulling some of the waiting bytes and you can retrieve more in a next call. Just pick whatever buffer size you can afford.

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

For SOCK_DGRAMsocket, you will get the fitting part of the waiting message and the rest will be discarded. You can get the waiting datagram size with the following ioctl:

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

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

Alternatively you can use MSG_PEEKand MSG_TRUNCflags of the recv()call to obtain the waiting datagram size.

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

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

You need MSG_PEEKto peek (not receive) the waiting message - recv returns the real, not truncated size; and you need MSG_TRUNCto not overflow your current buffer.

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

Then you can just malloc(size)the real buffer and recv()datagram.

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

回答by YeenFei

There is no absolute answer to your question, because technology is always bound to be implementation-specific. I am assuming you are communicating in UDP because incoming buffer size does not bring problem to TCP communication.

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

According to RFC 768, the packet size (header-inclusive) for UDP can range from 8 to 65 515 bytes. So the fail-proof size for incoming buffer is 65 507 bytes (~64KB)

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

However, not all large packets can be properly routed by network devices, refer to existing discussion for more information:

然而,并不是所有的大数据包都能被网络设备正确路由,更多信息请参考现有讨论:

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

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

回答by Andrew McGregor

16kb is about right; if you're using gigabit ethernet, each packet could be 9kb in size.

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