首页 > 解决方案 > 套接字缓冲区在 SCM_RIGHTS 类型时会发送消息吗?

问题描述

我写了一个 SCM_RIGHTS 类型的服务器和客户端,用于在进程之间发送 fd。

服务器

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string>
#include "TBufferCommon.h"
#include <iostream>

int m_bindSocket;
bool start()
{
    std::string bindSocketPath = "/tmp/test";
    if (access(bindSocketPath.c_str(), F_OK) == 0)
    {
        remove(bindSocketPath.c_str());
    }
    int retCode = 0;
    retCode = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (retCode == -1)
    {
        printf("%s failed to setup TBufferClient recv socket , error:  %s\n", __PRETTY_FUNCTION__, strerror(errno));
        return false;
    }

    m_bindSocket = retCode;
    sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    memcpy(addr.sun_path, bindSocketPath.c_str(), bindSocketPath.size());
    retCode = bind(m_bindSocket, (const sockaddr *)&addr, sizeof(addr));
    if (retCode == -1)
    {
        printf("%s failed to bind TBufferClient recv socket , error:  %s\n", __PRETTY_FUNCTION__, strerror(errno));
        return false;
    }
    return true;
}
    
bool recvBufferAndFd(void *buffer, int &buff_size, int &fd_recv)
{
    msghdr msg;
    do {
        iovec iov[1];

        char control[CMSG_SPACE(sizeof(int))];

        msg.msg_control = control;
        msg.msg_controllen = sizeof(control);
        msg.msg_name = NULL;
        msg.msg_namelen = 0;

        iov[0].iov_base = buffer;
        iov[0].iov_len = buff_size;
        msg.msg_iov = iov;
        msg.msg_iovlen = 1;

        ssize_t status = recvmsg(m_bindSocket, &msg, MSG_DONTWAIT);
        if ((status <= 0) && (errno != EAGAIN)) {
            printf("%s : recvmsg failed, with error = %s\n", __PRETTY_FUNCTION__, strerror(errno));
            return false;
        }
        BufferInfo *tmp = (BufferInfo*)buffer;
        printf("ckt test:recvBufferAndFd framecount %lu\n", tmp->frameCount);
    } while (errno != EAGAIN);
    printf("ckt test: erron: %d\n", errno);
    cmsghdr* cmsgPtr = CMSG_FIRSTHDR(&msg);
    fd_recv = *((int *)CMSG_DATA(cmsgPtr));
    return true;
}

int main(int argc, char const *argv[])
{
    BufferInfo info;
    int len =  sizeof(info);
    int fd;
    start();
    while (1)
    {
        recvBufferAndFd(&info, len, fd);
        std::cout << "frameCount: " << info.frameCount << " len: " << len << " fd: " << fd << std::endl;
        sleep(5);
        close(fd);
    }
    
    return 0;
}

客户

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string>
#include "TBufferCommon.h"
 
int retCode = 0;
void start(std::string destName) {
    int sendSocket;
    retCode = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (retCode == -1) {
        printf("failed to setup TBufferServer send socket , error:  %s\n", strerror(errno));
        return;
    }
    sendSocket = retCode;
    sockaddr_un addr;
    (void)memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    memcpy(addr.sun_path, destName.c_str(), destName.size());
    int conCode = connect(sendSocket, (const sockaddr *)&addr, sizeof(addr));
    if (conCode != 0) {
        close(sendSocket);
        printf("failed to conect TBufferServer send socket , error:  %s\n", strerror(errno));
        return;
    }
}

const bool sendBufferAndFdWithDestName(void *buf, int buf_size, int fd_sent)
{

    msghdr msg;
    iovec iov[1];

    char control[CMSG_SPACE(sizeof(int))];
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    cmsghdr *cmsgPtr = CMSG_FIRSTHDR(&msg);
    cmsgPtr->cmsg_len = CMSG_LEN(sizeof(int));
    cmsgPtr->cmsg_level = SOL_SOCKET;
    cmsgPtr->cmsg_type = SCM_RIGHTS;
    *((int *)CMSG_DATA(cmsgPtr)) = fd_sent;

    msg.msg_name = NULL;
    msg.msg_namelen = 0;

    iov[0].iov_base = buf;
    iov[0].iov_len = buf_size;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    int sndCode = sendmsg(retCode, &msg, MSG_DONTWAIT);
    if (sndCode <= 0)
    {
        printf("%s : sendmsg failed, with error =   %s\n", __PRETTY_FUNCTION__, strerror(errno));
        close(retCode);
        return false;
    }

    return true;
}

int main(void) {
    BufferInfo info;
    info.frameCount = 0;
    start("/tmp/test");
    while (1) {
        ++info.frameCount;
        if(sendBufferAndFdWithDestName(&info, sizeof(info), 1)) {
            printf("sendBufferAndFdWithDestName: frameCount: %lu\n", info.frameCount);
        }
        sleep(4);
    }
    close(retCode);
}

您可以看到客户端发送频率大于服务器接收频率。但是我使用 do-while 循环将旧消息转储到接收缓冲区中,所以期望输出打印应该像:

  1. 客户端的 frameCount 比服务器增加快 2 或 3;
  2. 但是服务器比丢弃一些旧消息然后赶上客户端。

但事实是下面的程序打印日志:

sendBufferAndFdWithDestName: frameCount: 49
ckt test:recvBufferAndFd framecount 39
ckt test: erron: 11
frameCount: 39 len: 32 fd: 4
sendBufferAndFdWithDestName: frameCount: 50
sendBufferAndFdWithDestName: frameCount: 51
ckt test:recvBufferAndFd framecount 40
ckt test: erron: 11
frameCount: 40 len: 32 fd: 4
sendBufferAndFdWithDestName: frameCount: 52
ckt test:recvBufferAndFd framecount 41
ckt test: erron: 11
frameCount: 41 len: 32 fd: 4
sendBufferAndFdWithDestName: frameCount: 53
ckt test:recvBufferAndFd framecount 42
ckt test: erron: 11
frameCount: 42 len: 32 fd: 4

从日志中,它显示服务器没有收到消息,甚至客户端已经发送。
当我将 msg_control 设置为 NULL 时,输出打印与我期望的相同。
所以我的问题是:套接字缓冲区在 SCM_RIGHTS 类型时会发送消息吗?
感谢您的回复。

BR/蒂姆

标签: sockets

解决方案


推荐阅读