我知道使用recv时不推荐使用小buffer,但是我找不到有关发送的任何信息。例如,在常见的平台上,每个send呼叫是否进入内核模式?在正常情况下,一个1字节的发送是否会导致发送一个1字节的数据包?
考虑到正常的接口类型是以太网,并且它的最大数据包大小为1500字节,因此通常TCP不会发送大于此大小的片段。并且它通常具有8Kb每个连接的内部缓冲区,因此没有必要增加内核空间的缓冲区大小(如果这是内核空间中缓冲区存在的唯一原因)。
当然,还有其他因素迫使你使用用户空间的缓冲区(例如,你想在某些地方存储要发送给对等进程的数据,因为内核空间中只有8Kb的数据可缓冲,而你需要更多的空间来执行其他进程)。例如:IRC服务器使用高达100Kb写缓冲区,在对面没有接收/确认该数据时会断开连接。如果你只使用 write(2) 连接,一旦内核缓冲区满了,你就会被等待,这可能不是你想要的。
在用户空间拥有缓冲区的原因是TCP还进行流量控制,所以当它无法发送数据时,必须将其放在某个地方以应对此情况。你需要决定是否需要让你的进程将数据保存到一定限制或者你可以阻塞发送数据,直到接收方再次能够接收数据。内核空间中的缓冲区大小受限且通常无法由用户/开发人员控制。用户空间中的缓冲区大小仅受其允许的资源限制。
不推荐在TCP连接中接收/发送小块数据,因为TCP握手和头部的增加会导致额外的开销。假设一个telnet连接,在每个字符发送时都会添加一个TCP头和另一个IP头(TCP最小20字节,IP最小20字节,以太网帧14字节和以太网CRC 4字节),这会导致要发送一个字符就需要60个字节+。通常每个TCP片段都是单独确认的,因此需要一次完整的往返时间来发送一个片段并获得确认(只为释放缓冲区资源并假定该字符已传输)。
那么,最终的限制是什么?这取决于您的应用程序。如果您可以处理内核可用的资源并且不需要更多的缓冲区,则可以在不具有用户空间缓冲区的情况下通过。如果您需要更多,则需要实现缓冲区,并能够在可用时将您的缓冲数据提供给内核缓冲区。
是的,在非常普通的情况下,一个字节的send
可以导致只有单个字节负载的TCP数据包被发送。在TCP中,发送合并通常通过使用Nagle算法来完成。通过 Nagle 算法,当已经发送但未被确认的数据存在时,将延迟发送数据。
相反,如果没有未确认的数据,则会立即发送数据。这通常适用于以下情况:
在这种情况下,您的应用程序执行的第一个send
调用将立即导致发送一个数据包,无论大小如何。因此,使用两个或多个小的send
开始通信通常是一个不好的想法,因为它会增加开销和延迟。
臭名昭著的“send send recv”模式也可能导致非常大的延迟(例如在Windows上通常为200毫秒)。如果本地TCP堆栈使用Nagle算法(通常会延迟第二个发送),并且远程堆栈使用延迟确认(可以延迟第一个数据包的确认),就会发生这种情况。
由于大多数TCP堆栈实现都同时使用Nagle算法和延迟确认,因此最好避免使用此模式。