为什么Sendto()系统调用不返回发送的字节数?

时间:2019-11-19 23:02:18

标签: sockets go network-programming system-calls

在Go的标准库中,网络系统调用Sendto()如下所示:

Windows

func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (err error)

Unix

func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error)

但是,底层系统调用返回所有操作系统(LinuxWindows)上已发送字节的数量,那么Go为什么只返回错误?

1 个答案:

答案 0 :(得分:3)

它确实应该-但通常,您可以在没有它的情况下逃脱。

我在评论中说,返回值“通常”很重要,但是这可能太过分了。请注意,每个操作系统在这里可能会有细微的差异。我将描述很久以前的传统行为:

    流类型的已连接套接字(带有sendtoSOCK_STREAM等的AF_UNIX上的
  1. AF_INETsend基本上相同:它循环,发送部分数据,直到发生一些有趣的事件。有趣的事件包括但不限于这些:

    • 所有数据已发送
    • 信号中断发送
    • 连接被另一端重置


    此时sendto调用返回。如果没有发送数据,则返回值为-1,错误为EINTR,如果没有发送所有数据,则返回值为短计数。此行为与write系统调用的行为相同。

  2. sendto在流类型但未连接的套接字上以ENOTCONN失败。

  3. sendto上已连接的数据报套接字错误,并抱怨已连接(EISCONN)。

  4. 未连接的数据报套接字上的
  5. sendto暂时将其连接(在通话期间),将消息作为单个数据报发送,并成功并返回发送的长度,或者失败并什么都不发送并返回-1和EMSGSIZE或其他一些更适当的错误(例如,如果连接失败或目标主机拒绝该数据包等,尽管并非所有这些错误都可能在所有协议上发生)。

    sendto套接字上的
  6. SOCK_SEQPACKET(类似于流的打包实体)的行为类似于SOCK_STREAM,除了整个消息作为单个数据包发送或发送完全失败。

因此,除了情况1 — sendto套接字上的SOCK_STREAM可能会中断之外,返回值始终为len-1。对于情况1,您可以只调用writeEINTR这种情况永远不要在Go中正常使用,因为Go运行时会将所有信号定向到进程内的专用OS级线程,这意味着只有获得一些数据时,您才能获得短暂的回报。发送,然后远程主机突然关闭(并重置)流。即使发生EINTR ,操作系统write也会产生正确的返回值。

sendmsg系统调用更为复杂,因为它允许这么多的标志,并且在不同的系统上具有不同的返回值。 BSD文档说它返回发送的消息数量,而Linux文档说它返回发送的 bytes 数量。各种消息标志也相当依赖于OS。像Go这样的简单包装器并不能真正轻松地隐藏这些差异。 (但是,我仍然看到Go库中有SendmsgN。)