sendmsg如何工作?

时间:2010-11-23 17:16:45

标签: c sockets send

如您所知 sendmsg 有此声明:

int sendmsg(int s, const struct msghdr *msg, int flags);

msghdr 结构具有以下形式:

struct msghdr {
    void         * msg_name;     /* optional address */
    socklen_t    msg_namelen;    /* size of address */
    struct iovec * msg_iov;      /* scatter/gather array */
    size_t       msg_iovlen;     /* # elements in msg_iov */
    void         * msg_control;  /* ancillary data, see below */
    socklen_t    msg_controllen; /* ancillary data buffer len */
    int          msg_flags;      /* flags on received message */
};

如你所见,msghdr有一个缓冲区数组,iovec并且缓冲区数为msg_iovlen。我想知道sendmsg如何发送这些缓冲区。它是连接所有缓冲区并发送还是以for循环发送?

3 个答案:

答案 0 :(得分:25)

该联机帮助页提到了一条消息(单数)和多个元素(复数):

  

对于send()sendto(),邮件位于buf并且有长度   len。对于sendmsg(),消息由元素指向   数组msg.msg_iovsendmsg()调用还允许发送辅助   数据(也称为控制信息)。

对于流套接字,无论哪种方式都无关紧要。您发送的任何数据最终都会成为另一方的一个长数据流。

对于数据报或消息套接字,我可以看到为什么更清晰一点会有所帮助。但似乎只发送一个sndmsg次呼叫的数据报或消息;每个缓冲元素不是一个。

我实际上是出于好奇而挖掘了Linux源代码,并对这个答案有了更好的认识。它看起来像send,而sendto只是Linux中sendmsg的包装器,可以为您构建struct msghdr。事实上,UDP sendmsg实现为每个sendmsg调用一个 UDP标头腾出了空间。

如果您担心表现,如果只传入一个sendmsg,您似乎不会从iovec中受益。但是,如果你在用户空间中连接缓冲区,这可能会让你获胜。

它有点类似于writev,还有一个额外的好处,即您可以指定一个目标地址,以便与UDP等无连接套接字一起使用。如果您涉及这类问题,您还可以添加辅助数据。 (通常用于跨UNIX域套接字发送文件描述符。)

答案 1 :(得分:1)

根据http://opengroup.org/onlinepubs/007908799/xns/sendmsg.html ...

  

每个存储区域的数据   msg_iov表示依次发送。

我的解释是sendmsg()不会连接存储在iovec中的消息数据;每个都将作为单独的消息发送。

[编辑:我的解释不正确;请参阅其他答案以获得更好的解释。]

答案 2 :(得分:1)

这取决于您的TCP / IP堆栈。嵌入式TCP / IP堆栈可能会将不同的iovec直接发送到NIC。但是在通常的TCP / IP堆栈上,必须已经存在从用户空间内存到内核空间内存的副本,因此没有任何增益,并且iovecs在概念上被复制到单个大块内存(它可以是单独的内存页面,如果驱动程序支持收集/收集I / O,但这里的重要部分是 iovec边界不会被保留)。