• 传递文件描述符


    传递的实质

    一个进程向另一个进程传递文件描述符时,实质是传递并共享同一文件描述符的表项, 也就是共享文件指针的当前位置/文件状态标志等
    在技术实现上就是把文件表项的指针传递给另一个进程
    通常发送进程与接受进程对传递的文件描述符的编号(int fd)是不一样的

    涉及的结构和函数

    要发送描述符,需要用sendmsg函数,sendmsg函数里的消息参数是struct msghdr, 而fd的相关信息保存在msghdr里面的另一个结构cmsghdr中
    由于cmsghdr的结构对齐原因,要正确取出里面的数据需要调用相应的宏

    ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    
    struct msghdr {
        void            *msg_name;         /* optional address */
        socklen_t        msg_namelen;      /* address size in bytes */
        struct iovec    *msg_iov;          /* array of I/O buffers */
        int              msg_iovlen;       /* number of elements in array */
        void            *msg_control;      /* ancillary data */
        socklen_t        msg_controllen;   /* number of ancillary bytes */
        int              msg_flags;        /* flags for received message */
    };
    
    struct iovec {
        void *iov_base;   /* Starting address */
        size_t iov_len;   /* Number of bytes */
    };
    
    struct cmsghdr {
        socklen_t        cmsg_len;      /* data byte count, including header */
        int              cmsg_level;    /* originating protocol */
        int              cmsg_type;     /* protocol-specific type */
        /* unsigned char *newfd;    fd数据保存在这里*/
    };
    
    unsigned char *CMSG_DATA(struct cmsghdr *cp);
    返回值:指向与cmsghdr结构相关联的数据的指针, 即上面结构中的unsigned char *newfd部分
     
    struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mp);
    返回值:指向与msghdr结构相关联的第一个cmsghdr结构的指针,若无这样的结构则返回NULL
     
    struct cmsghdr *CMSG_NXTHDR(struct msghdr *mp, struct cmsghdr *cp);
    返回值:指向与msghdr结构相关联的下一个cmsghdr结构的指针,该msghdr结构给出了当前cmsghdr结构,若当前cmsghdr结构已是最后一个则返回NULL
     
    unsigned int CMSG_LEN(unsigned int nbytes);
    返回值:为nbytes大小的数据对象分配的长度
    

    子进程向父进程传递文件描述符

    #include <sys/socket.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>
     
    static const int CONTROL_LEN = CMSG_LEN(sizeof(int));
     
    void send_fd(int fd, int fd_to_send){
        struct iovec iov[1];
        struct msghdr msg;
        char buf[0];
     
        iov[0].iov_base = buf;
        iov[0].iov_len = 1;
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_iov = iov;
        msg.msg_iovlen = 1;
     
        struct cmsghdr cm;
        cm.cmsg_len = CONTROL_LEN;
        cm.cmsg_level = SOL_SOCKET;
        cm.cmsg_type = SCM_RIGHTS;
        *(int*)CMSG_DATA(&cm) = fd_to_send;
        msg.msg_control = &cm; /*设置辅助数据*/
        msg.msg_controllen = CONTROL_LEN;
     
        sendmsg(fd, &msg, 0);
    }
     
    /*接收文件描述符*/
    int recv_fd(int fd){
        struct iovec iov[1];
        struct msghdr msg;
        char buf[0];
     
        iov[0].iov_base = buf;
        iov[0].iov_len = 1;
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_iov = iov;
        msg.msg_iovlen = 1;
     
        struct cmsghdr cm;
        msg.msg_control = &cm;
        msg.msg_controllen = CONTROL_LEN;
     
        recvmsg(fd, &msg, 0);
     
        int fd_to_read = *(int*)CMSG_DATA(&cm);
     
        return fd_to_read;
    }
     
    int main(int argc, char* argv[]){
        int pipefd[2];
        int ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, pipefd);
        assert(ret != -1);
     
        pid_t pid = fork();
        if(pid == 0)    {
            close(pipefd[0]);
            int fd_to_pass = open("passfd.c", O_RDONLY,0666);
     
            send_fd(pipefd[1], (fd_to_pass > 0) ? fd_to_pass : 0);
            close(fd_to_pass);
     
            exit(0);
        }
     
        close(pipefd[1]);
        int fd_recived = recv_fd(pipefd[0]);
        char buf[1024];
        memset(buf, '', 1024);
        read(fd_recived, buf, 1024);
        printf("I got fd %d and data %s
    ", fd_recived, buf);
     
        close(fd_recived);
     
        return 0;
    }
    

    unix域传递描述符

    在echo例子上多了一步, client连接到server后, server将一个fd传送给client, client读取fd内容后先转换成大写再发送到server, server收到后回射回来
    fileno(FILE *fp): FILE *fp转换到int fd的形式
    Fdopen(int fd,const char mode): int fd转换到FILE *fp的形式

    client.c

    #include <unistd.h>
    #include <errno.h>
    #include <ctype.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <netinet/in.h>
     
    #define MAXLINE 1024
    #define UN_PATH "/tmp/un_path" 
    static const int CONTROL_LEN = CMSG_LEN(sizeof(int));
     
    void err_quit(const char *s){
        perror(s);
        exit(1);
    }
     
    int recv_fd(int fd){
        struct iovec iov[1];
        struct msghdr msg;
        char buf[0];
     
        iov[0].iov_base=buf;
        iov[0].iov_len=1;
        msg.msg_name=NULL;
        msg.msg_namelen=0;
        msg.msg_iov=iov;
        msg.msg_iovlen=1;
     
        struct cmsghdr cm;
        msg.msg_control=&cm;
        msg.msg_controllen=CONTROL_LEN;
     
        recvmsg(fd,&msg,0);
        int fd_to_read=*(int*)CMSG_DATA(&cm);
     
        return fd_to_read;
    }
    void cli_echo(int sockfd){
        int n;
        char sendline[MAXLINE],recvline[MAXLINE];
     
        int recvfd=recv_fd(sockfd);
        FILE *fp=fdopen(recvfd,"r");
        if(fp == NULL)
            err_quit("fdopen");
     
        while(fgets(sendline,MAXLINE,fp) != NULL){
            bzero(recvline,sizeof(recvline));
            int i;
            for(i=0;i<strlen(sendline);++i)
                sendline[i]=toupper(sendline[i]);
            write(sockfd,sendline,strlen(sendline));
            n=read(sockfd,recvline,MAXLINE);
            if(n == -1){
                if(errno == EINTR)
                    continue;
                else
                    err_quit("write");
            }
            if(n == 0){
                puts("peer closed");
                return ;
            }
            recvline[n]=0;
            fputs(recvline,stdout);
        }
        fclose(fp);
    }
     
    int main(int argc,char *argv[]){
        int sockfd;
        struct sockaddr_un servaddr;
     
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sun_family=AF_LOCAL;
        strcpy(servaddr.sun_path,UN_PATH);
     
        sockfd=socket(AF_LOCAL,SOCK_STREAM,0);
        if(sockfd == -1)
            err_quit("socket");
     
        if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
                err_quit("connect");
     
        cli_echo(sockfd);
     
        return 0;
    }
    

    server.c

    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <signal.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <sys/un.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
     
    #define MAXLINE 1024
    #define UN_PATH "/tmp/un_path"
    static const int CONTROL_LEN = CMSG_LEN(sizeof(int));
     
    void send_fd(int fd,int fd_to_send){
        struct iovec iov[1];
        struct msghdr msg;
        char buf[0];
     
        iov[0].iov_base=buf;
        iov[0].iov_len=1;
        msg.msg_name=NULL;
        msg.msg_namelen=0;
        msg.msg_iov=iov;
        msg.msg_iovlen=1;
     
        struct cmsghdr cm;
        cm.cmsg_len=CONTROL_LEN;
        cm.cmsg_level=SOL_SOCKET;
        cm.cmsg_type=SCM_RIGHTS;
        *(int *)CMSG_DATA(&cm)=fd_to_send;
        msg.msg_control=&cm;
        msg.msg_controllen=CONTROL_LEN;
     
        sendmsg(fd,&msg,0);
    }
    void err_quit(const char *s){
        perror(s);
        exit(1);
    }
     
    void serv_echo(int sockfd){
        int n;
        char mesg[MAXLINE];
     
        int fd=open("server.c",O_RDONLY);
        if(fd < 0)
            err_quit("open");
     
        send_fd(sockfd,fd);
        close(fd);
        for(;;){
            bzero(mesg,sizeof(mesg));
            n=read(sockfd,mesg,MAXLINE);
            if(n == -1){
                if(errno == EINTR)
                    continue;
                else
                    err_quit("recvfrom");
            }
            if(n == 0){
                puts("peer closed");
                return ;
            }
            fputs(mesg,stdout);
            write(sockfd,mesg,n);
        }
    }
     
     
    void sig_chld(int signo){
        while(waitpid(-1,NULL,WNOHANG) > 0)
            ;
    }
     
    int main(int argc,char *argv[]){
        int sockfd,connfd;
        pid_t pid;
        struct sockaddr_un servaddr;
     
        sockfd=socket(AF_LOCAL,SOCK_STREAM,0);
     
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sun_family=AF_LOCAL;
        unlink(UN_PATH);
        strcpy(servaddr.sun_path,UN_PATH);
     
        if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
            err_quit("bind");
     
        if(listen(sockfd,SOMAXCONN) < 0)
            err_quit("listen");
     
        signal(SIGCHLD,sig_chld);
     
        while(1){
            connfd=accept(sockfd,NULL,NULL);
            if(connfd == -1){
                if(errno == EINTR)
                    continue;
                else
                    err_quit("accept");
            }
     
            pid=fork();
            if(pid == -1)
                err_quit("fork");
            else if(pid == 0){
                close(sockfd);  
                serv_echo(connfd);
                exit(0);
            }
     
            close(connfd);
        }
        return 0;
    }
    
  • 相关阅读:
    除了类 Excel, SpreadJS 表格控件还能为系统开发带来什么价值?
    纯前端表格控件SpreadJS V14.0发布:组件化编辑器+数据透视表
    攻克金融系统开发难点,借助SpreadJS实现在线导入Excel自定义报表
    50.Pyinstaller打包时出现:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce...
    centos 查看日志
    TP5.1 控制器(基类)
    tp5.1 微信支付、支付宝、招商支付(Payment)
    TP5.1 发送邮件
    tp5.1 模型集成
    TP5.1 阿里云短信
  • 原文地址:https://www.cnblogs.com/cfans1993/p/6144819.html
Copyright © 2020-2023  润新知