Ubuntu Linux使用Unix域套接字发送文件描述符

我试图发送一个文件描述符与下面粘贴的代码套接字。 此代码来自: http : //www.thomasstover.com/uds.html 。 我在Ubuntu 16.04 64位上运行。

问题是收到我的程序运行的文件描述符是“3”而不是“4”。 在接收过程中,我也无法读取任何数据。 为什么它不工作?

控制台输出如下所示:

Parent at work FILE TO SEND HAS DESCRIPTOR: 4 Parent read: [[hello phil ]] Child at play Read 3! Done: 0 Success! Parent exits 

码:

 #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/wait.h> #include <time.h> #include <unistd.h> #include <errno.h> int send_fd(int socket, int fd_to_send) { struct msghdr socket_message; struct iovec io_vector[1]; struct cmsghdr *control_message = NULL; char message_buffer[1]; /* storage space needed for an ancillary element with a paylod of length is CMSG_SPACE(sizeof(length)) */ char ancillary_element_buffer[CMSG_SPACE(sizeof(int))]; int available_ancillary_element_buffer_space; /* at least one vector of one byte must be sent */ message_buffer[0] = 'F'; io_vector[0].iov_base = message_buffer; io_vector[0].iov_len = 1; /* initialize socket message */ memset(&socket_message, 0, sizeof(struct msghdr)); socket_message.msg_iov = io_vector; socket_message.msg_iovlen = 1; /* provide space for the ancillary data */ available_ancillary_element_buffer_space = CMSG_SPACE(sizeof(int)); memset(ancillary_element_buffer, 0, available_ancillary_element_buffer_space); socket_message.msg_control = ancillary_element_buffer; socket_message.msg_controllen = available_ancillary_element_buffer_space; /* initialize a single ancillary data element for fd passing */ control_message = CMSG_FIRSTHDR(&socket_message); control_message->cmsg_level = SOL_SOCKET; control_message->cmsg_type = SCM_RIGHTS; control_message->cmsg_len = CMSG_LEN(sizeof(int)); *((int *) CMSG_DATA(control_message)) = fd_to_send; return sendmsg(socket, &socket_message, 0); } int recv_fd(int socket) { int sent_fd, available_ancillary_element_buffer_space; struct msghdr socket_message; struct iovec io_vector[1]; struct cmsghdr *control_message = NULL; char message_buffer[1]; char ancillary_element_buffer[CMSG_SPACE(sizeof(int))]; /* start clean */ memset(&socket_message, 0, sizeof(struct msghdr)); memset(ancillary_element_buffer, 0, CMSG_SPACE(sizeof(int))); /* setup a place to fill in message contents */ io_vector[0].iov_base = message_buffer; io_vector[0].iov_len = 1; socket_message.msg_iov = io_vector; socket_message.msg_iovlen = 1; /* provide space for the ancillary data */ socket_message.msg_control = ancillary_element_buffer; socket_message.msg_controllen = CMSG_SPACE(sizeof(int)); if(recvmsg(socket, &socket_message, MSG_CMSG_CLOEXEC) < 0) return -1; if(message_buffer[0] != 'F') { /* this did not originate from the above function */ return -1; } if((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC) { /* we did not provide enough space for the ancillary element array */ return -1; } /* iterate ancillary elements */ for(control_message = CMSG_FIRSTHDR(&socket_message); control_message != NULL; control_message = CMSG_NXTHDR(&socket_message, control_message)) { if( (control_message->cmsg_level == SOL_SOCKET) && (control_message->cmsg_type == SCM_RIGHTS) ) { sent_fd = *((int *) CMSG_DATA(control_message)); return sent_fd; } } return -1; } int main(int argc, char **argv) { const char *filename = "/tmp/z7.c"; if (argc > 1) filename = argv[1]; int sv[2]; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) != 0) fprintf(stderr,"Failed to create Unix-domain socket pair\n"); int pid = fork(); if (pid > 0) // in parent { fprintf(stderr,"Parent at work\n"); close(sv[1]); int sock = sv[0]; int fd = open(filename, O_RDONLY); if (fd < 0) fprintf(stderr,"Failed to open file %s for reading %s\n", filename, strerror(errno)); fprintf(stderr,"FILE TO SEND HAS DESCRIPTOR: %d\n",fd); /* Read some data to demonstrate that file offset is passed */ char buffer[32]; int nbytes = read(fd, buffer, sizeof(buffer)); if (nbytes > 0) fprintf(stderr,"Parent read: [[%.*s]]\n", nbytes, buffer); send_fd(sock, fd); close(fd); sleep(4); fprintf(stderr,"Parent exits\n"); } else // in child { fprintf(stderr,"Child at play\n"); close(sv[0]); int sock = sv[1]; sleep(2); int fd = recv_fd(sock); printf("Read %d!\n", fd); char buffer[256]; ssize_t nbytes; while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0) { fprintf(stderr,"WRITING: %d\n",nbytes); write(1, buffer, nbytes); } printf("Done: %d %s!\n",nbytes,strerror(errno)); close(fd); } return 0; } 

文件偏移由两个进程共享。 所以当父进程读取到EOF时,子进程没有任何内容可以读取。

这与两个进程从父进程继承文件描述符(例如shell命令)相同:

 { echo first cat; cat ; echo second cat ; cat ; } < filename 

第一个cat命令将读取所有文件,第二个cat将没有任何可读的内容。

引用Richard Stevens(编程UNIX网络):

“接收过程中的描述符号码与发送过程中的描述符号码是不同的,传递一个描述符不是通过描述符号码,而是在接收过程中创建一个新的描述符指向相同的文件入口在内核中作为发送进程发送的描述符。“