linux进程间传递文件描述符

48 阅读3分钟

原文链接:使用 Unix Domain Sockets 在不同进程间传递文件句柄 - shensunbo Blog

我的仓库地址:gitee.com/coderkemi/i…

传递单个句柄

sender

sender.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>

#define SOCKET_NAME "/tmp/my_socket"

void send_fd(int socket, int fd_to_send) {
    const char *str1 = "Hello, ";
    struct msghdr msg = {};
    struct iovec io = { .iov_base = (void *)str1, .iov_len = 1 };
    char control[CMSG_SPACE(sizeof(int))];
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

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

    if (sendmsg(socket, &msg, 0) == -1) {
        perror("sendmsg");
    }
}

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un server_addr = { .sun_family = AF_UNIX, .sun_path = SOCKET_NAME };

    unlink(SOCKET_NAME);
    bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    listen(sockfd, 1);

    printf("Awaiting connection...\n");

    int client_sock = accept(sockfd, NULL, NULL);
    printf("Connection accepted!\n");

    int file_fd = open("example.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
    send_fd(client_sock, file_fd);
    printf("File descriptor sent!\n");

    close(file_fd);
    close(client_sock);
    close(sockfd);
    return 0;
}

receiver

receiver.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>

#define SOCKET_NAME "/tmp/my_socket"

int receive_fd(int socket) {
    struct msghdr msg = {};
    char buf[1];
    struct iovec io = { .iov_base = buf, .iov_len = sizeof(buf) };
    char control[CMSG_SPACE(sizeof(int))];
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    if (recvmsg(socket, &msg, 0) <= 0) {
        perror("recvmsg");
        return -1;
    }

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
    return *((int *)CMSG_DATA(cmsg));
}

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un server_addr = { .sun_family = AF_UNIX, .sun_path = SOCKET_NAME };

    connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    printf("Connected to sender!\n");

    int fd_received = receive_fd(sockfd);
    if (fd_received != -1) {
        printf("File descriptor received: %d\n", fd_received);

        // Use the file descriptor (e.g., read/write to the file it points to)
        // Don't forget to close it eventually
        char buffer[100];
        read(fd_received, buffer, sizeof(buffer));
        printf("Read from file: %s\n", buffer);
        close(fd_received);
    }

    close(sockfd);
    return 0;
}

编译

gcc -o receiver.exe receiver.c

gcc -o sender.exe sender.c

运行

创建 example.txt

quark@quarkpi-ca2:~$ cat example.txt
file123abc
quark@quarkpi-ca2:~$

运行 sender.exe

quark@quarkpi-ca2:~$ ./sender.exe
Awaiting connection...
Connection accepted!
File descriptor sent!
quark@quarkpi-ca2:~$

运行 receiver.exe

quark@quarkpi-ca2:~$ ./receiver.exe
Connected to sender!
File descriptor received: 4
Read from file: file123abc

quark@quarkpi-ca2:~$

传递多个句柄

sender

sender.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define SOCKET_NAME "/tmp/my_socket"

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        return 1;
    }

    struct sockaddr_un server_addr = { .sun_family = AF_UNIX };
    strncpy(server_addr.sun_path, SOCKET_NAME, sizeof(server_addr.sun_path) - 1);

    unlink(SOCKET_NAME);
    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        close(sockfd);
        return 1;
    }

    if (listen(sockfd, 1) == -1) {
        perror("listen");
        close(sockfd);
        return 1;
    }

    printf("Awaiting connection...\n");
    int client_sock = accept(sockfd, NULL, NULL);
    if (client_sock == -1) {
        perror("accept");
        close(sockfd);
        return 1;
    }
    printf("Connection accepted!\n");

    int file_fd = open("example.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
    int file_fd2 = open("log1.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
    int file_fd3 = open("log2.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
    int file_fd4 = open("log3.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);

    char dummy_data = 'X';
    struct iovec io = { .iov_base = &dummy_data, .iov_len = 1 };
    
    char cmsgbuf[CMSG_SPACE(sizeof(int) * 4)];
    struct msghdr msg = {};
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_control = cmsgbuf;
    msg.msg_controllen = sizeof(cmsgbuf);

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 4);
    int *fdptr = (int *)CMSG_DATA(cmsg);
    fdptr[0] = file_fd;
    fdptr[1] = file_fd2;
    fdptr[2] = file_fd3;
    fdptr[3] = file_fd4;

    printf("File descriptors to send: %d %d %d %d\n", file_fd, file_fd2, file_fd3, file_fd4);

    if (sendmsg(client_sock, &msg, 0) == -1) {
        perror("sendmsg");
        fprintf(stderr, "errno: %d\n", errno);
    } else {
        printf("File descriptors sent successfully!\n");
    }

    close(file_fd);
    close(file_fd2);
    close(file_fd3);
    close(file_fd4);
    close(client_sock);
    close(sockfd);
    return 0;
}

receiver

receiver.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>

#define SOCKET_NAME "/tmp/my_socket"

int main() {
    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    // struct sockaddr_un server_addr = { .sun_family = AF_UNIX, .sun_path = SOCKET_NAME };

    struct sockaddr_un server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_NAME, sizeof(server_addr.sun_path) - 1);

    if(0 != connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))){
        printf("connect error!\n");
        return -1;
    }
    printf("Connected to sender!\n");

    // int fd_received = receive_fd(sockfd);
    struct msghdr msg = {};
    char buf[1];
    struct iovec io = { .iov_base = buf, .iov_len = sizeof(buf) };
    char control[CMSG_SPACE(sizeof(int) * 4)];
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    if (recvmsg(sockfd, &msg, 0) <= 0) {
        perror("recvmsg");
        return -1;
    }

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
    int* fdptr = ((int *)CMSG_DATA(cmsg));

    printf("File descriptor received: %d %d %d %d\n", fdptr[0], fdptr[1], fdptr[2], fdptr[3]);

    // Use the file descriptor (e.g., read/write to the file it points to)
    // Don't forget to close it eventually
    char buffer[100];
    read(fdptr[0], buffer, sizeof(buffer));
    printf("Read from file 0: %s\n", buffer);
    close(fdptr[0]);

    read(fdptr[1], buffer, sizeof(buffer));
    printf("Read from file 1: %s\n", buffer);
    close(fdptr[1]);

    read(fdptr[2], buffer, sizeof(buffer));
    printf("Read from file 2: %s\n", buffer);
    close(fdptr[2]);

    read(fdptr[3], buffer, sizeof(buffer));
    printf("Read from file 3: %s\n", buffer);
    close(fdptr[3]);

    close(sockfd);
    return 0;
}

编译

gcc -o receiver.exe receiver.c

gcc -o sender.exe sender.c

运行

创建 example.txt

quark@quarkpi-ca2:~$ cat example.txt
file123abc
quark@quarkpi-ca2:~$

创建 log1.txt

quark@quarkpi-ca2:~$ cat log1.txt
11111
quark@quarkpi-ca2:~$

创建 log2.txt

quark@quarkpi-ca2:~$ cat log2.txt
22222
quark@quarkpi-ca2:~$

创建 log3.txt

quark@quarkpi-ca2:~$ cat log3.txt
33333
quark@quarkpi-ca2:~$

运行 sender.exe

quark@quarkpi-ca2:~$ ./sender.exe
Awaiting connection...
Connection accepted!
File descriptors to send: 5 6 7 8
File descriptors sent successfully!
quark@quarkpi-ca2:~$

运行 receiver.exe

quark@quarkpi-ca2:~$ ./receiver.exe
Connected to sender!
File descriptor received: 4 5 6 7
Read from file 0: file123abc

Read from file 1: 11111
3abc

Read from file 2: 22222
3abc

Read from file 3: 33333
3abc

quark@quarkpi-ca2:~$