在 C Linux 套接字中使用 iovec 传递多个缓冲区

问题描述

我正在编写一个 linux C 客户端-服务器程序,该程序通过 unix 域套接字相互通信并每次传递几个缓冲区。 我正在使用 ioverctors 但由于某些原因服务器程序只接收第一个 io 向量。 任何的想法 ? 我附上了相关的代码片段。

客户端代码

$excludes = '/stocklist/','/assets/','.bk'
# create a regex of the folders to exclude
# each folder will be Regex Escaped and joined together with the OR symbol '|'
$notthese = ($excludes | ForEach-Object { [Regex]::Escape($_) }) -join '|'

Get-ChildItem -Path $pathName -Filter 'index.aspx*' -File -Recurse |
Where-Object{ $_.DirectoryName -notmatch $notthese -and $_.Name -notmatch '^\s*_' }

我从服务器得到的输出: 接收: abc 16 16

解决方法

sendmsg()writev()pwritev()pwritev2() 不操作多个缓冲区,而是一个不连续的缓冲区。它们的运行方式与您分配足够大的临时缓冲区一样,在那里收集数据,然后对单个临时缓冲区执行相应的系统调用。

它们的对应对象 recvmsg()readv()preadv()preadv2() 类似地不操作多个缓冲区,仅操作一个不连续的缓冲区。它们的运行方式与您分配足够大的临时缓冲区一样,将数据接收到该缓冲区中,然后将数据从该缓冲区分散到不连续的缓冲区部分。

Unix domain 数据报 (SOCK_DGRAM) 和 seqpacket (SOCK_SEQPACKET) 套接字保留消息边界,但流套接字 (SOCK_STREAM) 不保留。也就是说,使用数据报或 seqpacket 套接字,您可以在发送时接收每条消息。使用流套接字,消息边界会丢失:可以将两条连续发送的消息作为一条消息接收,并且您(至少在理论上)现在可以接收部分消息,然后再接收其余消息。

您可以使用 Linux 特定的 sendmmsg() 函数在一次调用中发送多条消息(使用同一个套接字)。如果您使用 Unix 域数据报或 seqpacket 套接字,则这些将保留其消息边界。

每条消息都使用 struct mmsghdr 进行描述。它包含 struct msghdr msg_hdr;unsigned int msg_len;msg_hdr 与您在使用例如发送单个消息时使用的相同sendmsg();您可以为每条消息使用多个 iovec,但接收者将接收它们连接到单个缓冲区中(但可以使用例如 recvmsg() 分散该缓冲区)。 msg_len 将由 sendmmsg() 调用填充:为该特定消息发送的字节数,类似于例如的返回值sendmsg() 在没有错误发生时调用。

sendmmsg() 调用的返回值是成功发送的消息数(可能少于请求的数量!),如果发生错误,则为 -1errno 表示像往常一样错误)。因此,您需要编写一个辅助函数或围绕 sendmmsg() 的循环以确保发送所有消息。为了可移植性,我推荐一个辅助函数,因为当 sendmsg() 不可用时,您可以基于围绕 sendmmsg() 的循环提供另一个函数。

sendmmsg() 唯一真正的好处是您需要更少的系统调用来发送大量消息:它在某些情况下提高了效率,仅此而已。