在 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() 唯一真正的好处是发送大量消息所需的系统调用更少:它在某些情况下提高了效率,仅此而已。