进程间传递文件描述符--sendmsg,recvmsg,struct msghdr,struct iovec

UNIX域套接字可以在同一台主机上各进程之间传递文件描述符。

下面先来看两个函数:

[cpp]  view plain  copy
  1. #include <sys/types.h>   
  2. #include <sys/socket.h>   
  3.   
  4. ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);   
  5. ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);  

它们与sendto 和 recvfrom 函数相似,只不过可以传输更复杂的数据结构,不仅可以传输一般数据,还可以传输额外的数据,即文件描述符。下面来看结构体msghdr :

[cpp]  view plain  copy
  1. struct msghdr {   
  2.     void         *msg_name;       /* optional address */   
  3.     socklen_t     msg_namelen;    /* size of address */   
  4.     struct iovec *msg_iov;        /* scatter/gather array */   
  5.     size_t        msg_iovlen;     /* # elements in msg_iov */   
  6.     void         *msg_control;    /* ancillary data, see below */   
  7.     size_t        msg_controllen; /* ancillary data buffer len */   
  8.     int           msg_flags;      /* flags on received message */   
  9. };   

1、msg_name :即对等方的地址指针,不关心时设为NULL即可;

2、msg_namelen:地址长度,不关心时设置为0即可;

3、msg_iov:是结构体iovec 的指针。   

[cpp]  view plain  copy
  1. struct iovec {   
  2.                void  *iov_base;    /* Starting address */   
  3.                size_t iov_len;     /* Number of bytes to transfer */   
  4.            };  

成员iov_base 可以认为是传输正常数据时的buf,iov_len 是buf 的大小。

4、msg_iovlen:当有n个iovec 结构体时,此值为n;

5、msg_control:是一个指向cmsghdr 结构体的指针

[cpp]  view plain  copy
  1. struct cmsghdr {   
  2.           socklen_t cmsg_len;    /* data byte count, including header */   
  3.           int       cmsg_level;  /* originating protocol */   
  4.           int       cmsg_type;   /* protocol-specific type */   
  5.           /* followed by unsigned char cmsg_data[]; */   
  6.       };   

6、msg_controllen :cmsghdr 结构体可能不止一个;

7、flags : 一般设置为0即可;


直接上代码:

client.c

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <stddef.h>  
  5. #include <string.h>  
  6. #include <sys/types.h>  
  7. #include <errno.h>  
  8. #include <sys/un.h>  
  9. #include <sys/socket.h>  
  10. #include <netinet/in.h>  
  11. #include <sys/epoll.h>  
  12. #include <fcntl.h>  
  13.   
  14. #define UNIXSTR_PATH "foo.socket"  
  15. #define OPEN_FILE  "test"  
  16.   
  17. int main(int argc, char *argv[])  
  18. {  
  19.     int clifd;  
  20.     struct sockaddr_un servaddr;  //IPC  
  21.     int ret;  
  22.     struct msghdr msg;  
  23.     struct iovec iov[1];  
  24.     char buf[100];  
  25.     union {  //保证cmsghdr和msg_control对齐  
  26.         struct cmsghdr cm;  
  27.         char control[CMSG_SPACE(sizeof(int))];  
  28.     } control_un;  
  29.     struct cmsghdr *pcmsg;  
  30.     int fd;  
  31.   
  32.     clifd  = socket(AF_UNIX, SOCK_STREAM, 0) ;  
  33.     if   ( clifd  <  0 ) {  
  34.         printf ( "socket failed.\n" ) ;  
  35.         return  - 1 ;  
  36.     }  
  37.   
  38.     fd  =  open(OPEN_FILE ,O_CREAT | O_RDWR, 0777);  
  39.     if( fd  <  0 ) {  
  40.         printf("open test failed.\n");  
  41.         return -1;  
  42.     }  
  43.   
  44.     bzero (&servaddr, sizeof(servaddr));  
  45.     servaddr.sun_family = AF_UNIX;  
  46.     strcpy ( servaddr.sun_path, UNIXSTR_PATH);  
  47.   
  48.     ret = connect(clifd, (struct sockaddr*)&servaddr, sizeof(servaddr));  
  49.     if(ret < 0) {  
  50.         printf ( "connect failed.\n" ) ;  
  51.         return 0;  
  52.     }  
  53.     //udp需要,tcp无视  
  54.     msg.msg_name = NULL;  
  55.     msg.msg_namelen = 0;  
  56.     iov[0].iov_base = buf;  
  57.     iov[0].iov_len = 100;  
  58.     msg.msg_iov = iov;  
  59.     msg.msg_iovlen = 1;  
  60.     //设置缓冲区和长度  
  61.     msg.msg_control = control_un.control;  
  62.     msg.msg_controllen = sizeof(control_un.control);  
  63.     //直接通过CMSG_FIRSTHDR取得附属数据  
  64.     pcmsg = CMSG_FIRSTHDR(&msg);  
  65.     pcmsg->cmsg_len = CMSG_LEN(sizeof(int));  
  66.     pcmsg->cmsg_level = SOL_SOCKET;  
  67.     pcmsg->cmsg_type = SCM_RIGHTS;  //指明发送的是描述符  
  68.     *((int*)CMSG_DATA(pcmsg)) == fd;  //把描述符写入辅助数据  
  69.   
  70.     ret = sendmsg(clifd, &msg, 0);  //send filedescriptor  
  71.     printf ("ret = %d, filedescriptor = %d\n", ret, fd);  
  72.     return 0 ;  
  73. }  

server.c

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h>  
  4. #include <stddef.h>  
  5. #include <string.h>  
  6. #include <sys/types.h>  
  7. #include <errno.h>  
  8. #include <sys/un.h>  
  9. #include <sys/socket.h>  
  10. #include <netinet/in.h>  
  11. #include <sys/epoll.h>  
  12. #include <fcntl.h>  
  13.   
  14. #define UNIXSTR_PATH "foo.socket"  
  15.   
  16. int main(int argc, char *argv[])  
  17. {  
  18.     int clifd, listenfd;  
  19.     struct sockaddr_un servaddr, cliaddr;  
  20.     int ret;  
  21.     socklen_t clilen;  
  22.     struct msghdr msg;  
  23.     struct iovec iov[1];  
  24.     char buf [100];  
  25.     char  *testmsg = "test msg.\n";  
  26.   
  27.     union {  //对齐  
  28.         struct cmsghdr cm;  
  29.         char control[CMSG_SPACE(sizeof(int))];  
  30.     }  control_un;  
  31.     struct cmsghdr  * pcmsg;  
  32.     int  recvfd;  
  33.   
  34.     listenfd  =  socket ( AF_UNIX ,  SOCK_STREAM ,  0 ) ;  
  35.     if(listenfd < 0) {  
  36.         printf ( "socket failed.\n" ) ;  
  37.         return  -1;  
  38.     }  
  39.   
  40.     unlink(UNIXSTR_PATH) ;  
  41.   
  42.     bzero (&servaddr, sizeof(servaddr));  
  43.     servaddr.sun_family = AF_UNIX;  
  44.     strcpy ( servaddr.sun_path ,  UNIXSTR_PATH ) ;  
  45.   
  46.     ret  =  bind ( listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));  
  47.     if(ret < 0) {  
  48.         printf ( "bind failed. errno = %d.\n" ,  errno ) ;  
  49.         close(listenfd);  
  50.         return  - 1 ;  
  51.     }  
  52.   
  53.     listen(listenfd, 5);  
  54.   
  55.     while(1) {  
  56.         clilen = sizeof( cliaddr );  
  57.         clifd = accept( listenfd, (struct sockaddr*)&cliaddr , &clilen);  
  58.         if ( clifd < 0 ) {  
  59.             printf ( "accept failed.\n" ) ;  
  60.             continue ;  
  61.         }  
  62.   
  63.         msg.msg_name  = NULL;  
  64.         msg.msg_namelen  = 0;  
  65.         //设置数据缓冲区  
  66.         iov[0].iov_base = buf;  
  67.         iov[0].iov_len = 100;  
  68.         msg.msg_iov = iov;  
  69.         msg.msg_iovlen = 1;  
  70.         //设置辅助数据缓冲区和长度  
  71.         msg.msg_control = control_un.control;  
  72.         msg.msg_controllen  =  sizeof(control_un.control) ;  
  73.         //接收  
  74.         ret = recvmsg(clifd , &msg, 0);  
  75.         if( ret <= 0 ) {  
  76.             return ret;  
  77.         }  
  78.         //检查是否收到了辅助数据,以及长度  
  79.         if((pcmsg = CMSG_FIRSTHDR(&msg) ) != NULL && ( pcmsg->cmsg_len == CMSG_LEN(sizeof(int)))) {  
  80.             if ( pcmsg->cmsg_level  !=  SOL_SOCKET ) {  
  81.                 printf("cmsg_leval is not SOL_SOCKET\n");  
  82.                 continue;  
  83.             }  
  84.   
  85.             if ( pcmsg->cmsg_type != SCM_RIGHTS ) {  
  86.                 printf ( "cmsg_type is not SCM_RIGHTS" );  
  87.                 continue;  
  88.             }  
  89.             //这就是我们接收的描述符  
  90.             recvfd = *((int*)CMSG_DATA(pcmsg));  
  91.             printf ( "recv fd = %d\n", recvfd );  
  92.   
  93.             write ( recvfd, testmsg, strlen(testmsg) + 1);  
  94.         }  
  95.     }  
  96.   
  97.     return 0 ;  
  98. }  



进程间传输文件描述符的fd是不一定相同的,在client里面是4,到达server的时候4已经被占用了,所以分配了5给它。

猜你喜欢

转载自blog.csdn.net/handsomehong/article/details/80019934