【问题标题】:Partial read/write issue in sendmsg/recvmsgsendmsg/recvmsg 中的部分读/写问题
【发布时间】:2016-11-20 18:22:24
【问题描述】:

我正在编写一个程序,该程序使用sendmsgrecvmsg 通过域套接字在两个进程之间传递文件描述符。为了发送文件描述符,msghdr.msg_iovmsghdr.msg_iolen 中包含附加数据。但是,我被告知类似于正常的readwrite 系统调用,sendmsgrecvmsg 也有部分读/写问题。在这种情况下,辅助字段中的数据是否会为每个部分数据自动复制?我问这个是因为我的实现需要非阻塞机制。让我用下面的例子来详细说明一下

发送者:发送msghdr数据,其中包含一个fd在辅助字段和K字节在msg_iov

接收方:(1) 部分读取,K1 字节 (2) 部分读取,K-K1 字节

现在和上面的例子一样,当所有数据到达时,我实际上应该处理步骤(2)之后的数据。在这种情况下,我还能从辅助字段中正确提取fd 吗?还是只出现在第一次部分阅读中?

【问题讨论】:

  • 您将通过其返回值发送sendmsg() 指示的尽可能多的字节,并通过其返回值接收recvmsg() 指示的尽可能多的字节。在这两种情况下,您都必须循环以确保完整的传输和接收。
  • 在两个不同进程之间发送文件描述符不起作用。文件描述符对于进程来说是本地的,就像指针一样。
  • @KlasLindbäck 在两个不同进程之间发送文件描述符不起作用。文件描述符是进程本地的,就像指针一样。 That is not correct. 可以使用 Unix 域套接字在进程之间发送文件描述符。根据Linux man pageUNIX 域套接字支持使用辅助数据将文件描述符或进程凭据传递给其他进程。
  • 感谢@AndrewHenle 指出这一点。
  • Unix & Linux 也有类似的问题:unix.stackexchange.com/questions/185011/…

标签: c sockets unix-socket


【解决方案1】:

快速浏览内核源代码(Linux,但见下文),我相信您有责任确保辅助数据只发送一次。也就是说,在非阻塞模式下,如果接收套接字没有空间,你将返回EAGAIN/EWOULDBLOCK,既不会发送数据,也不会发送辅助数据。但是如果接收端有一些空间,那么数据的初始部分将被发送,辅助数据也将被发送。然后,您将收到一个返回字节计数,指示部分发送,但辅助数据将已发送。

当您尝试发送消息的其余部分时,您需要注意这一点,因为内核不会保留您之前发送的部分缓冲区的内存,随后的缓冲区在逻辑上是连续的(真的不可能- 你可能会发送完全不同的数据,因为它知道)。因此,如果您只是为后续缓冲区部分提供相同的辅助数据,我相信内核会很高兴地与您后续的缓冲区部分再次提供辅助数据。这可能会导致接收方的文件描述符重复(您可能会忽略关闭,因为您不会期望它们)——如果您不避免的话。

现在,如果您在发送端处于阻塞模式,并且传输被分成多个部分,则辅助数据将仅发送一次 - 与第一个缓冲区部分一样,因为整个缓冲区的发送仍在内部内核控制。

因此,在接收方,如果您没有收到完整的逻辑消息,您需要注意辅助数据伴随着第一块接收到的数据。

我相信这种行为与@Klas-Lindbäck (https://unix.stackexchange.com/questions/185011/what-happens-with-unix-stream-ancillary-data-on-partial-reads) 给出的 stackexchange 参考中报告的一致。 (不过,这个问题并没有涉及非阻塞模式。)

这个答案是特定于 linux 的。因此,结果肯定有可能在其他操作系统上略有不同,尽管我很难看出它们如何有显着不同并且仍然保持理智的语义。内核无法合理地维护先前发送的内容的内存,sendmsg 原型不允许它覆盖用户的msghdr 以反映msg_control 部分已经发送。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-02
    • 2014-09-28
    • 2021-04-09
    • 1970-01-01
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多