如您所知 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循环发送?
答案 0 :(得分:25)
该联机帮助页提到了一条消息(单数)和多个元素(复数):
对于
send()
和sendto()
,邮件位于buf
并且有长度len
。对于sendmsg()
,消息由元素指向 数组msg.msg_iov
。sendmsg()
调用还允许发送辅助 数据(也称为控制信息)。
对于流套接字,无论哪种方式都无关紧要。您发送的任何数据最终都会成为另一方的一个长数据流。
对于数据报或消息套接字,我可以看到为什么更清晰一点会有所帮助。但似乎只发送一个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边界不会被保留)。