C语言 sendmsg 如何工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4258834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How sendmsg works?
提问by whoi
As you know sendmsghas this declaration:
如您所知,sendmsg有以下声明:
int sendmsg(int s, const struct msghdr *msg, int flags);
int sendmsg(int s, const struct msghdr *msg, int flags);
and msghdrstructure has this form:
和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 */
};
As you see msghdr has an array of buffer, iovec and has buffer count msg_iovlen. What I wonder is that how sendmsg sends these buffers. Does it concatenate all buffers and send or does it send in a for loop?
如您所见,msghdr 有一个缓冲区数组 iovec,并且缓冲区计数为 msg_iovlen。我想知道的是 sendmsg 如何发送这些缓冲区。它是连接所有缓冲区并发送还是在 for 循环中发送?
回答by Stéphan Kochen
The manpage speaks of a message (singular) and multiple elements (plural):
联机帮助页谈到消息(单数)和多个元素(复数):
For
send()andsendto(), the message is found inbufand has lengthlen. Forsendmsg(), the message is pointed to by the elements of the arraymsg.msg_iov. Thesendmsg()call also allows sending ancillary data (also known as control information).
对于
send()和sendto(),消息位于buf并且具有长度len。对于sendmsg(),消息由数组的元素指向msg.msg_iov。该sendmsg()调用还允许发送辅助数据(也称为控制信息)。
For a stream socket, it wouldn't matter either way. Any data you send will just end up as one long stream of data on the other side.
对于流套接字,无论哪种方式都无关紧要。您发送的任何数据都将在另一端变成一个长数据流。
For datagram or message sockets, I can see why a bit more clarity would be helpful. But it appears that you send just one datagram or message with a single sndmsgcall; not one per buffer element.
对于数据报或消息套接字,我可以理解为什么更清晰一点会有所帮助。但看起来你一次sndmsg调用只发送了一个数据报或消息;不是每个缓冲区元素一个。
I actually went digging in the Linux source code out of curiosity and to get a better feeling about this answer. It looks like send, and sendtoare just wrappers for sendmsgin Linux, that build the struct msghdrfor you. And in fact, the UDP sendmsgimplementation makes room for oneUDP header per sendmsgcall.
我实际上是出于好奇而深入研究 Linux 源代码,以便更好地了解这个答案。它看起来像send,并且sendto只是sendmsgLinux 中的包装器,可以struct msghdr为您构建。事实上,UDPsendmsg实现为每个调用留出了一个UDP 标头sendmsg。
If performance is what you're worried about, it doesn't look like you'll benefit from sendmsgif you pass in just a single iovec. If you're concatenating buffers in user-space, though, this could potentially win you some.
如果性能是您所担心的,那么sendmsg如果您只传入一个iovec. 但是,如果您在用户空间中连接缓冲区,这可能会给您带来一些好处。
It's a bit similar to writev, with the added benefit that you can specify a destination address for use with connectionless sockets like UDP. You can also add ancillary data, if you're into that sort of thing. (Commonly used to send file descriptors across UNIX domain sockets.)
它有点类似于writev,还有一个额外的好处,即您可以指定一个目标地址,以便与 UDP 等无连接套接字一起使用。如果您喜欢这类事情,您还可以添加辅助数据。(通常用于跨 UNIX 域套接字发送文件描述符。)
回答by Kamal
According to http://opengroup.org/onlinepubs/007908799/xns/sendmsg.html...
根据http://opengroup.org/onlinepubs/007908799/xns/sendmsg.html...
The data from each storage area indicated by msg_iov is sent in turn.
msg_iov 指示的每个存储区的数据依次发送。
My interpretation is that sendmsg()will not concatenate the message data stored in the iovec's; each will be sent as a separate message.
我的解释是sendmsg()不会连接iovec中存储的消息数据;每个将作为单独的消息发送。
[Edit: My interpretation was not correct; see the other answers for a better explanation.]
[编辑:我的解释不正确;请参阅其他答案以获得更好的解释。]
回答by ninjalj
It depends on your TCP/IP stack. Embedded TCP/IP stacks could potentially send the different iovecs directly to the NIC. But on usual TCP/IP stacks there must already be a copy from userspace memory to kernelspace memory, so there is no gain there, and iovecs get conceptually copied to a single big chunk of memory (it can be separate pages of memory, if the driver supports scather/gather I/O, but the important part here is that iovec boundaries don't get preserved).
这取决于您的 TCP/IP 堆栈。嵌入式 TCP/IP 堆栈可能会将不同的 iovec 直接发送到 NIC。但是在通常的 TCP/IP 堆栈上,必须已经有从用户空间内存到内核空间内存的副本,因此没有任何好处,并且 iovecs 在概念上被复制到一个大内存块(它可以是单独的内存页,如果驱动程序支持 scather/gather I/O,但这里的重要部分是不会保留 iovec 边界)。

