UNIX域套接字可以在同一台主机上各进程之间传递文件描述符。
下面先来看两个函数:
- #include <sys/types.h>
- #include <sys/socket.h>
- ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
- ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
它们与sendto 和 recvfrom 函数相似,只不过可以传输更复杂的数据结构,不仅可以传输一般数据,还可以传输额外的数据,即文件描述符。下面来看结构体msghdr :
- struct msghdr {
- void *msg_name; /* optional address */
- socklen_t msg_namelen; /* size of address */
- struct iovec *msg_iov; /* scatter/gather array */
- size_t msg_iovlen; /* # elements in msg_iov */
- void *msg_control; /* ancillary data, see below */
- size_t msg_controllen; /* ancillary data buffer len */
- int msg_flags; /* flags on received message */
- };
1、msg_name :即对等方的地址指针,不关心时设为NULL即可;
2、msg_namelen:地址长度,不关心时设置为0即可;
3、msg_iov:是结构体iovec 的指针。
- struct iovec {
- void *iov_base; /* Starting address */
- size_t iov_len; /* Number of bytes to transfer */
- };
成员iov_base 可以认为是传输正常数据时的buf,iov_len 是buf 的大小。
4、msg_iovlen:当有n个iovec 结构体时,此值为n;
5、msg_control:是一个指向cmsghdr 结构体的指针
- struct cmsghdr {
- socklen_t cmsg_len; /* data byte count, including header */
- int cmsg_level; /* originating protocol */
- int cmsg_type; /* protocol-specific type */
- /* followed by unsigned char cmsg_data[]; */
- };
6、msg_controllen :cmsghdr 结构体可能不止一个;
7、flags : 一般设置为0即可;
直接上代码:
client.c
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <string.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <sys/un.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <sys/epoll.h>
- #include <fcntl.h>
- #define UNIXSTR_PATH "foo.socket"
- #define OPEN_FILE "test"
- int main(int argc, char *argv[])
- {
- int clifd;
- struct sockaddr_un servaddr; //IPC
- int ret;
- struct msghdr msg;
- struct iovec iov[1];
- char buf[100];
- union { //保证cmsghdr和msg_control对齐
- struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(int))];
- } control_un;
- struct cmsghdr *pcmsg;
- int fd;
- clifd = socket(AF_UNIX, SOCK_STREAM, 0) ;
- if ( clifd < 0 ) {
- printf ( "socket failed.\n" ) ;
- return - 1 ;
- }
- fd = open(OPEN_FILE ,O_CREAT | O_RDWR, 0777);
- if( fd < 0 ) {
- printf("open test failed.\n");
- return -1;
- }
- bzero (&servaddr, sizeof(servaddr));
- servaddr.sun_family = AF_UNIX;
- strcpy ( servaddr.sun_path, UNIXSTR_PATH);
- ret = connect(clifd, (struct sockaddr*)&servaddr, sizeof(servaddr));
- if(ret < 0) {
- printf ( "connect failed.\n" ) ;
- return 0;
- }
- //udp需要,tcp无视
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- iov[0].iov_base = buf;
- iov[0].iov_len = 100;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- //设置缓冲区和长度
- msg.msg_control = control_un.control;
- msg.msg_controllen = sizeof(control_un.control);
- //直接通过CMSG_FIRSTHDR取得附属数据
- pcmsg = CMSG_FIRSTHDR(&msg);
- pcmsg->cmsg_len = CMSG_LEN(sizeof(int));
- pcmsg->cmsg_level = SOL_SOCKET;
- pcmsg->cmsg_type = SCM_RIGHTS; //指明发送的是描述符
- *((int*)CMSG_DATA(pcmsg)) == fd; //把描述符写入辅助数据
- ret = sendmsg(clifd, &msg, 0); //send filedescriptor
- printf ("ret = %d, filedescriptor = %d\n", ret, fd);
- return 0 ;
- }
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <string.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <sys/un.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <sys/epoll.h>
- #include <fcntl.h>
- #define UNIXSTR_PATH "foo.socket"
- int main(int argc, char *argv[])
- {
- int clifd, listenfd;
- struct sockaddr_un servaddr, cliaddr;
- int ret;
- socklen_t clilen;
- struct msghdr msg;
- struct iovec iov[1];
- char buf [100];
- char *testmsg = "test msg.\n";
- union { //对齐
- struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(int))];
- } control_un;
- struct cmsghdr * pcmsg;
- int recvfd;
- listenfd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
- if(listenfd < 0) {
- printf ( "socket failed.\n" ) ;
- return -1;
- }
- unlink(UNIXSTR_PATH) ;
- bzero (&servaddr, sizeof(servaddr));
- servaddr.sun_family = AF_UNIX;
- strcpy ( servaddr.sun_path , UNIXSTR_PATH ) ;
- ret = bind ( listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
- if(ret < 0) {
- printf ( "bind failed. errno = %d.\n" , errno ) ;
- close(listenfd);
- return - 1 ;
- }
- listen(listenfd, 5);
- while(1) {
- clilen = sizeof( cliaddr );
- clifd = accept( listenfd, (struct sockaddr*)&cliaddr , &clilen);
- if ( clifd < 0 ) {
- printf ( "accept failed.\n" ) ;
- continue ;
- }
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- //设置数据缓冲区
- iov[0].iov_base = buf;
- iov[0].iov_len = 100;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- //设置辅助数据缓冲区和长度
- msg.msg_control = control_un.control;
- msg.msg_controllen = sizeof(control_un.control) ;
- //接收
- ret = recvmsg(clifd , &msg, 0);
- if( ret <= 0 ) {
- return ret;
- }
- //检查是否收到了辅助数据,以及长度
- if((pcmsg = CMSG_FIRSTHDR(&msg) ) != NULL && ( pcmsg->cmsg_len == CMSG_LEN(sizeof(int)))) {
- if ( pcmsg->cmsg_level != SOL_SOCKET ) {
- printf("cmsg_leval is not SOL_SOCKET\n");
- continue;
- }
- if ( pcmsg->cmsg_type != SCM_RIGHTS ) {
- printf ( "cmsg_type is not SCM_RIGHTS" );
- continue;
- }
- //这就是我们接收的描述符
- recvfd = *((int*)CMSG_DATA(pcmsg));
- printf ( "recv fd = %d\n", recvfd );
- write ( recvfd, testmsg, strlen(testmsg) + 1);
- }
- }
- return 0 ;
- }

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