在 C Linux 套接字中使用 iovec 传递多个缓冲区
Passing multiple buffers with iovec in C Linux sockets
我正在编写一个 linux C 客户端-服务器程序,它使用 unix 域套接字相互通信并每次传递几个缓冲区。
我正在使用 ioverctors 但由于某些原因服务器程序只接收第一个 io 向量。
任何想法 ?
我附上了相关的代码片段。
客户代码:
struct iovec iov[2];
struct msghdr mh;
int rc;
char str1[] = "abc";
char str2[] = "1234";
iov[0].iov_base = (caddr_t)str1;
iov[0].iov_len = sizeof(str1);
iov[1].iov_base = (caddr_t)str2;
iov[1].iov_len = sizeof(str2);
memset(&mh, 0, sizeof(mh));
mh.msg_iov = iov;
mh.msg_iovlen = 2;
n = sendmsg(sockfd, &mh, 0); /* no flags used*/
if (n > 0) {
printf("Sendmsg successfully executed\n");
}
}
Server code:
{
struct sockaddr_un *client_sockaddr = (sockaddr_un *)opq;
struct msghdr msg;
struct iovec io[2];
char buf[16];
char buf2[16];
io[0].iov_base = buf;
io[0].iov_len = sizeof(buf);
io[1].iov_base = buf2;
io[1].iov_len = sizeof(buf2);
msg.msg_iov = io;
msg.msg_iovlen = 2;
int len = recvmsg(sock, &msg, 0);
if (len > 0) {
printf("recv: %s %d %s %d\n", msg.msg_iov[0].iov_base, msg.msg_iov[0].iov_len, msg.msg_iov[1].iov_base, msg.msg_iov[1].iov_len);
}
return 0;
}
我从服务器得到的输出:
接收:abc 16 16
sendmsg(), writev(), pwritev(), and pwritev2()不对多个缓冲区进行操作,而是对一个不连续的缓冲区进行操作。它们的操作就像您分配足够大的临时缓冲区一样,在那里收集数据,然后在单个临时缓冲区上执行相应的系统调用。
他们的对手recvmsg(), readv(), preadv(), and 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()
调用填充:为该特定消息发送的字节数,类似于例如 return 的值sendmsg()
没有错误发生时调用。
sendmmsg()
调用的 return 值是成功发送消息的数量(可能少于请求的数量!),如果发生错误(使用 errno
照常指示错误)。因此,您需要编写辅助函数或围绕 sendmmsg()
的循环以确保发送所有消息。为了可移植性,我推荐一个辅助函数,因为当 sendmmsg()
不可用时,您可以基于围绕 sendmsg()
的循环提供另一个辅助函数。
sendmmsg()
唯一真正的好处是发送大量消息所需的系统调用更少:它在某些情况下提高了效率,仅此而已。
我正在编写一个 linux C 客户端-服务器程序,它使用 unix 域套接字相互通信并每次传递几个缓冲区。 我正在使用 ioverctors 但由于某些原因服务器程序只接收第一个 io 向量。 任何想法 ? 我附上了相关的代码片段。
客户代码:
struct iovec iov[2];
struct msghdr mh;
int rc;
char str1[] = "abc";
char str2[] = "1234";
iov[0].iov_base = (caddr_t)str1;
iov[0].iov_len = sizeof(str1);
iov[1].iov_base = (caddr_t)str2;
iov[1].iov_len = sizeof(str2);
memset(&mh, 0, sizeof(mh));
mh.msg_iov = iov;
mh.msg_iovlen = 2;
n = sendmsg(sockfd, &mh, 0); /* no flags used*/
if (n > 0) {
printf("Sendmsg successfully executed\n");
}
}
Server code:
{
struct sockaddr_un *client_sockaddr = (sockaddr_un *)opq;
struct msghdr msg;
struct iovec io[2];
char buf[16];
char buf2[16];
io[0].iov_base = buf;
io[0].iov_len = sizeof(buf);
io[1].iov_base = buf2;
io[1].iov_len = sizeof(buf2);
msg.msg_iov = io;
msg.msg_iovlen = 2;
int len = recvmsg(sock, &msg, 0);
if (len > 0) {
printf("recv: %s %d %s %d\n", msg.msg_iov[0].iov_base, msg.msg_iov[0].iov_len, msg.msg_iov[1].iov_base, msg.msg_iov[1].iov_len);
}
return 0;
}
我从服务器得到的输出: 接收:abc 16 16
sendmsg(), writev(), pwritev(), and pwritev2()不对多个缓冲区进行操作,而是对一个不连续的缓冲区进行操作。它们的操作就像您分配足够大的临时缓冲区一样,在那里收集数据,然后在单个临时缓冲区上执行相应的系统调用。
他们的对手recvmsg(), readv(), preadv(), and 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()
调用填充:为该特定消息发送的字节数,类似于例如 return 的值sendmsg()
没有错误发生时调用。
sendmmsg()
调用的 return 值是成功发送消息的数量(可能少于请求的数量!),如果发生错误(使用 errno
照常指示错误)。因此,您需要编写辅助函数或围绕 sendmmsg()
的循环以确保发送所有消息。为了可移植性,我推荐一个辅助函数,因为当 sendmmsg()
不可用时,您可以基于围绕 sendmsg()
的循环提供另一个辅助函数。
sendmmsg()
唯一真正的好处是发送大量消息所需的系统调用更少:它在某些情况下提高了效率,仅此而已。