首页 文章

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

提问于
浏览
20

我已经看到了一些关于讨论基础协议的 send() 的问题 . 我发送了's no guarantee that the receiver will get the message in one atomic operation. In this question I',只是在谈论 send() 系统调用的行为,因为它与本地系统的网络层交互 .

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

如果发送套接字上没有空间来保存要传输的消息,并且套接字文件描述符没有设置O_NONBLOCK,则send()将阻塞,直到空间可用 . 如果发送套接字上没有空间来保存要传输的消息,并且套接字文件描述符确实设置了O_NONBLOCK,则send()将失败 .

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

  • 如果内核中的套接字队列中有足够的空间用于整个消息并且没有信号发生(正常情况),则复制它并返回长度 .

  • 如果在 send() 期间发生信号,则必须返回 -1 . 显然,在这种情况下我们不能排队部分消息,因为我们不知道发送了多少消息 . 因此在这种情况下无法发送任何内容 .

  • 如果内核中的套接字队列中没有足够的空间用于整个消息并且套接字阻塞,那么根据上面的语句 send() 必须阻塞,直到空间可用 . 然后消息将排队, send() 返回长度 .

  • 如果内核中的套接字队列中没有足够的空间用于整个消息且套接字是非阻塞的,则 send() 必须失败(返回 -1 ), errno 将设置为 EAGAINEWOULDBLOCK . 同样,由于我们返回 -1 ,很明显在这种情况下,消息的任何部分都不能排队 .

我错过了什么吗? send()是否可以返回 >=0 && <length 的值?在什么情况下?那么非POSIX / UNIX系统呢? Windows send() 实现是否符合此要求?

5 回答

  • 1

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

  • 4
    • 根据Posix规范和30年来我见过的所有man 2发送页面,是的, send() 可以返回任何值> 0和<= length . 请注意,它不能返回零 .

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

  • 0

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

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

  • 10

    send() 可以返回 value >= 0 && < length . 如果在调用 send() 时发送缓冲区的空间小于消息长度,则可能发生这种情况 . 类似地,如果发送方已知的当前接收方窗口大小小于消息的长度,则可以仅发送消息的一部分 . 有趣的是,当接收过程缓慢卸载从接收缓冲区接收的数据时,我已经看到这种情况发生在Linux上通过localhost连接 .

    我的感觉是,一个人的实际经验会因实施而有所不同 . 从this Microsoft link开始,很明显可以发生小于长度的非错误返回值 .

    它是如果发送零长度消息,也可以获得零的返回值(同样,至少对于某些实现) .

    这个答案是基于我的经验,特别是利用this SO answer .

    编辑:从this answer及其注释,显然只有在发送任何数据之前发生中断时才会导致 EINTR 失败,这将是获得此类返回值的另一种可能方式 .

  • 3

    澄清一点,它说:

    应阻止直到空间可用 .

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

    • 足够的空间可用 .

    • 信号中断当前阻塞操作 .
      为套接字设置

    • SO_SNDTIMEO ,超时到期 .

    • 其他,例如套接字在另一个线程中关闭 .

    所以事情就这样结束了:

    • 如果内核中的套接字队列中有足够的空间用于整个消息并且没有信号发生(正常情况),则复制它并返回长度 .

    • 如果在send()期间发生信号,则它必须返回-1 . 显然,在这种情况下我们不能排队部分消息,因为我们不知道发送了多少消息 . 因此在这种情况下无法发送任何内容 .

    • 如果内核中的套接字队列中没有足够的空间用于整个消息并且套接字阻塞,那么根据上面的语句,send()必须阻塞,直到空间可用 . 然后消息将排队,send()返回长度 . 然后 send() 可以被信号中断,发送超时可以过去......导致短发送/部分写入 . 如果没有将任何内容复制到发送缓冲区,则合理的实现将返回-1并将 errno 设置为适当的值 .

    • 如果内核中的套接字队列中没有足够的空间用于整个消息,并且套接字是非阻塞的,则send()必须失败(返回-1),并且errno将设置为EAGAIN或EWOULDBLOCK . 同样,由于我们返回-1,很明显在这种情况下,消息的任何部分都不能排队 .

相关问题