zoukankan      html  css  js  c++  java
  • 传递文件描述符

    传递的实质

    一个进程向另一个进程传递文件描述符时,实质是传递并共享同一文件描述符的表项, 也就是共享文件指针的当前位置/文件状态标志等
    在技术实现上就是把文件表项的指针传递给另一个进程
    通常发送进程与接受进程对传递的文件描述符的编号(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;
    }
    
  • 相关阅读:
    DataAnnotations
    使用BizTalk实现RosettaNet B2B So Easy
    biztalk rosettanet 自定义 pip code
    Debatching(Splitting) XML Message in Orchestration using DefaultPipeline
    Modifying namespace in XML document programmatically
    IIS各个版本中你需要知道的那些事儿
    关于IHttpModule的相关知识总结
    开发设计的一些思想总结
    《ASP.NET SignalR系列》第五课 在MVC中使用SignalR
    《ASP.NET SignalR系列》第四课 SignalR自托管(不用IIS)
  • 原文地址:https://www.cnblogs.com/cfans1993/p/6144819.html
Copyright © 2011-2022 走看看