可以在TCP套接字上发送()返回> = 0和

时间:2013-10-31 02:08:12

标签: c sockets tcp

我已经看到了一些关于讨论基础协议的send()的问题。我完全清楚,对于TCP,任何消息都可能在发送时被分解成部分,并且无法保证接收器将在一次原子操作中获取消息。在这个问题中,我只谈论send()系统调用的行为,因为它与本地系统的网络层交互。

根据POSIX标准和我读过的send()文档,要发送的消息的长度由 length 参数指定。请注意:send()发送一条长度长度消息。进一步:

  

如果发送套接字上没有空间来保留消息   传输,并且套接字文件描述符没有   O_NONBLOCK设置,send()将阻止,直到空间可用。如果空间   发送套接字不能保存消息   传输,套接字文件描述符确实有O_NONBLOCK设置,   send()将失败。

我认为send()的定义中没有任何可能返回-1以外的任何值(这意味着没有数据在内核中排队等待传输)或 length ,这意味着整个消息在内核中排队等待传输。即,在我看来send()必须是原子,关于本地排队消息以便在内核中传递

  1. 如果内核中的套接字队列中有足够的空间用于整个消息并且没有信号发生(正常情况),则复制它并返回 length
  2. 如果send()期间出现信号,则必须返回-1。显然,在这种情况下我们不能排队部分消息,因为我们不知道发送了多少消息。因此在这种情况下无法发送任何内容。
  3. 如果内核中的没有足够的空间用于整个消息并且套接字阻塞,那么根据上面的语句send()必须阻塞,直到空间可用。然后邮件将排队,send()返回 length
  4. 如果内核中的套接字队列中没有足够的空间用于整个消息,并且套接字是非阻塞的,那么send()必须失败(返回-1)和errno将设置为EAGAINEWOULDBLOCK。同样,由于我们返回-1,很明显在这种情况下,邮件的任何部分都不能排队。
  5. 我错过了什么吗? send()是否可以返回>=0 && <length的值?在什么情况下?那么非POSIX / UNIX系统呢? Windows send()实现是否符合此标准?

5 个答案:

答案 0 :(得分:10)

你的观点2过于简化了。 send返回大于零但小于长度的值的正常条件(注意,正如其他人所说,除了长度参数为零之外它永远不能返回零)是消息足够长的时候导致阻塞,并且在已经发送一些内容之后到达中断信号。在这种情况下,send EINTR不会失败(因为这会阻止应用程序知道它已经成功发送了一些数据)并且它无法重新阻塞(因为信号正在中断,整个这一点是为了摆脱阻塞),所以它必须返回已发送的字节数,这小于请求的总长度。

答案 1 :(得分:4)

  1. 根据Posix规范和我30年来见过的所有 man 2发送页面,是的,send()可以返回任何值&gt; 0和&lt; = length。请注意,无法返回零。

  2. 根据几年前有关新闻的讨论:comp.protocols.tcp-ip所有TCP实现者所在的位置,阻塞send()在转移所有数据之前不会实际返回到套接字发送缓冲区:换句话说,返回值是-1或length.同意所有已知的实现都是如此,write(), writev()也是如此, sendmsg()writev()

答案 2 :(得分:3)

我知道这个东西在Linux上是如何工作的,使用GNU C Library。在这种情况下,您问题的第4点会有不同的描述。如果为文件描述符设置了标志O_NONBLOCK,并且如果无法以原子方式将整个消息排入内核,send()将返回实际发送的字节数(可以在1到1之间)长度),errno设置为EWOULDBLOCK

(如果文件描述符在阻塞模式下工作,send()将阻止。)

答案 3 :(得分:0)

澄清一点,它说:

  

将阻止,直到空间可用。

有几种方法可以从该区块/睡眠中醒来:

  • 有足够的空间。
  • 信号中断当前的阻塞操作。
  • 为插槽设置了
  • SO_SNDTIMEO,超时到期。
  • 其他,例如套接字在另一个线程中关闭。

因此事情就这样结束了:

  1. 如果内核中的套接字队列中有足够的空间用于整个消息,并且没有信号发生(正常情况),则复制它并返回长度。
  2. 如果在send()期间发生信号,则必须返回-1。显然,在这种情况下我们不能排队部分消息,因为我们不知道发送了多少消息。因此,在这种情况下无法发送任何内容。
  3. 如果内核中的套接字队列中没有足够的空间用于整个消息并且套接字阻塞,那么根据上面的语句,send()必须阻塞,直到空间可用。 然后消息将排队并且send()返回长度。 然后send()可以被信号中断,发送超时可以过去,...导致短发送/部分写。如果没有将任何内容复制到发送缓冲区,则合理的实现将返回-1并将errno设置为适当的值。
  4. 如果内核中的套接字队列中没有足够的空间用于整个消息,并且套接字是非阻塞的,则send()必须失败(返回-1)并且errno将设置为EAGAIN或EWOULDBLOCK。同样,由于我们返回-1,很明显在这种情况下,消息的任何部分都不能排队。

答案 4 :(得分:0)

在64位Linux系统上:

sendto(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4294967296, 0, NULL, 0) = 2147479552

因此,即使试图发送低4GB的内容,Linux也会发送出去并发送少于2GB的内容。因此,如果您认为自己会要求它发送1TB,并且它会耐心地坐在那里,那就继续祝愿。

类似地,在只有几个KB可用空间的嵌入式系统上,不要认为它会失败或等待某件事-它会发送尽可能多的信息,并告诉您有多少信息,让您可以重试其余步骤(或在此期间进行其他操作)。

每个人都同意,在使用EINTR的情况下,可能会发送短消息。但是EINTR可以随时发生,因此总会发送短消息。

最后,POSIX说返回的字节数是句点。正式的Unix和POSIX都建立在短读/写概念的基础上,它允许POSIX系统的实现从最小的嵌入式扩展到带有众所周知的“大数据”的超级计算机。因此,无需尝试在各行之间阅读并沉迷于手头上的特定即席实现。还有更多的实现方式,只要您遵循标准一词,您的应用就可以在其中移植。