在socket库中调用recv时我的recv缓冲区应该有多大
我对 C 中的套接字库有几个问题。这是我将在问题中引用的代码片段。
char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
- 我如何决定recv_buffer有多大?我用的是3000,但它是任意的。
- 如果
recv()
收到的数据包大于我的缓冲区会发生什么? - 我怎样才能知道我是否已经收到整个消息而无需再次调用recv并在没有收到任何内容时让它永远等待?
- 有没有一种方法可以使缓冲区不具有固定的空间量,以便我可以不断添加内容而不必担心空间不足?也许使用
strcat
将最新的recv()
响应连接到缓冲区?
我知道这是一个很多问题,但我将非常感谢任何答复。
I have a few questions about the socket library in C. Here is a snippet of code I'll refer to in my questions.
char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
- How do I decide how big to make recv_buffer? I'm using 3000, but it's arbitrary.
- what happens if
recv()
receives a packet bigger than my buffer? - 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?
- 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
strcat
to concatenate the latestrecv()
response to the buffer?
I know it's a lot of questions in one, but I would greatly appreciate any responses.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这些问题的答案取决于您使用的是流套接字 (
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()
分配的,即)。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.How do you know how big to make the buffer passed to
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.What happens if
recv
gets a packet larger than the buffer?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 torecv
.SOCK_DGRAM
: The excess bytes are discarded.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 singlerecv
call always returns a single datagram.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 withmalloc()
orcalloc()
, that is).对于 TCP 等流协议,您几乎可以将缓冲区设置为任意大小。也就是说,建议使用 2 的幂的常用值,例如 4096 或 8192。
如果数据多于缓冲区的数据,它将简单地保存在内核中,供下次调用
recv
时使用。是的,您可以继续增加缓冲区。您可以从偏移量
idx
开始对缓冲区的中间进行接收,您可以这样做: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.
If there is more data then what your buffer, it will simply be saved in the kernel for your next call to
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:如果您有
SOCK_STREAM
套接字,recv
仅从流中获取“最多前 3000 个字节”。对于缓冲区的大小没有明确的指导:您知道流有多大的唯一时间是当它全部完成时;-)。如果您有一个
SOCK_DGRAM
套接字,并且数据报大于缓冲区,recv
将使用数据报的第一部分填充缓冲区,返回 -1,并将 errno 设置为EMSGSIZE。不幸的是,如果协议是 UDP,这意味着数据报的其余部分会丢失——这就是 UDP 被称为不可靠协议的部分原因(我知道有可靠的数据报协议,但它们不是很可靠)流行的——尽管我非常了解 TCP/IP 家族,但我无法说出 TCP/IP 家族中的一个;-)。要动态增长缓冲区,请首先使用
malloc
分配它,并根据需要使用realloc
。但这对您从 UDP 源进行recv
没有帮助,唉。If you have a
SOCK_STREAM
socket,recv
just 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;-).If you have a
SOCK_DGRAM
socket, and the datagram is larger than the buffer,recv
fills 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 unreliable protocol (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;-).To grow a buffer dynamically, allocate it initially with
malloc
and userealloc
as needed. But that won't help you withrecv
from a UDP source, alas.对于 SOCK_STREAM 套接字,缓冲区大小并不重要,因为您只是提取一些等待字节,并且可以在下一次调用中检索更多字节。只需选择您能承受的任何缓冲区大小即可。
对于
SOCK_DGRAM
套接字,您将获得等待消息的合适部分,其余部分将被丢弃。您可以使用以下 ioctl 获取等待数据报大小:或者,您可以使用
recv()
调用的MSG_PEEK
和MSG_TRUNC
标志来获取等待数据报大小。您需要 MSG_PEEK 来查看(而不是接收)等待消息 - receive 返回真实的、未截断的大小;并且您需要
MSG_TRUNC
才不会溢出当前缓冲区。然后你就可以
malloc(size)
真正的缓冲区和recv()
数据报。For
SOCK_STREAM
socket, 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.For
SOCK_DGRAM
socket, 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:Alternatively you can use
MSG_PEEK
andMSG_TRUNC
flags of therecv()
call to obtain the waiting datagram size.You need
MSG_PEEK
to peek (not receive) the waiting message - recv returns the real, not truncated size; and you needMSG_TRUNC
to not overflow your current buffer.Then you can just
malloc(size)
the real buffer andrecv()
datagram.您的问题没有绝对的答案,因为技术总是特定于实现的。我假设您正在使用 UDP 进行通信,因为传入缓冲区大小不会给 TCP 通信带来问题。
根据 RFC 768,数据包大小(包含标头) ) 对于 UDP,范围可以是 8 到 65 515 字节。因此,传入缓冲区的防故障大小为 65 507 字节(~64KB)。
但是,并非所有大数据包都可以由网络设备正确路由,请参阅现有讨论以获取更多信息:
实现最大吞吐量的 UDP 数据包的最佳大小是多少?
网络上最大的安全 UDP 数据包大小是多少互联网
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.
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)
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
16kb左右就可以了;如果您使用千兆位以太网,每个数据包的大小可能为 9kb。
16kb is about right; if you're using gigabit ethernet, each packet could be 9kb in size.