0

我正在编写一个 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;
}

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

4

1 回答 1

2

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

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

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

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

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

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

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

于 2021-07-21T12:26:19.683 回答