如何使用sendmsg()通过两个进程之间的套接字发送文件描述符?

在@cnicutar回答了这个问题之后 ,我尝试从父进程发送一个文件描述符给它的subprocess。 基于这个例子 ,我写了这个代码:

int socket_fd ,accepted_socket_fd, on = 1; int server_sd, worker_sd, pair_sd[2]; struct sockaddr_in client_address; struct sockaddr_in server_address; /* ======================================================================= * Setup the network socket. * ======================================================================= */ if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket()"); exit(EXIT_FAILURE); } if((setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) { perror("setsockopt()"); exit(EXIT_FAILURE); } server_address.sin_family = AF_INET; /* Internet address type */ server_address.sin_addr.s_addr = htonl(INADDR_ANY); /* Set for any local IP */ server_address.sin_port = htons(port); /* Set to the specified port */ if(bind(socket_fd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { perror("bind()"); exit(EXIT_FAILURE); } if(listen(socket_fd, buffers) < 0) { perror("listen()"); exit(EXIT_FAILURE); } if(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair_sd) < 0) { socketpair("bind()"); exit(EXIT_FAILURE); } server_sd = pair_sd[0]; worker_sd = pair_sd[1]; /* ======================================================================= * Worker processes * ======================================================================= */ struct iovec iov[1]; struct msghdr child_msg; char msg_buffer[80]; int pass_sd, rc; /* Here the parent process create a pool of worker processes (its children) */ for(i = 0; i < processes; i++) { if(fork() == 0) { // ... /* Loop forever, serving the incoming request */ for(;;) { memset(&child_msg, 0, sizeof(child_msg)); memset(iov, 0, sizeof(iov)); iov[0].iov_base = msg_buffer; iov[0].iov_len = sizeof(msg_buffer); child_msg.msg_iov = iov; child_msg.msg_iovlen = 1; child_msg.msg_name = (char *) &pass_sd; child_msg.msg_namelen = sizeof(pass_sd); printf("Waiting on recvmsg\n"); rc = recvmsg(worker_sd, &child_msg, 0); if (rc < 0) { perror("recvmsg() failed"); close(worker_sd); exit(-1); } else if (child_msg.msg_namelen <= 0) { printf("Descriptor was not received\n"); close(worker_sd); exit(-1); } else { printf("Received descriptor = %d\n", pass_sd); } //.. Here the child process can handle the passed file descriptor } } } /* ======================================================================= * The parent process * ======================================================================= */ struct msghdr parent_msg; size_t length; /* Here the parent will accept the incoming requests and passed it to its children*/ for(;;) { length = sizeof(client_address); if((accepted_socket_fd = accept(socket_fd, NULL, NULL)) < 0) { perror("accept()"); exit(EXIT_FAILURE); } memset(&parent_msg, 0, sizeof(parent_msg)); parent_msg.msg_name = (char *) &accepted_socket_fd; parent_msg.msg_namelen = sizeof(accepted_socket_fd); if((sendmsg(server_sd, &parent_msg, 0)) < 0) { perror("sendmsg()"); exit(EXIT_FAILURE); } } 

但不幸的是,我得到这个错误:

 sendmsg(): Invalid argument 

我该怎么办才能解决这个问题? 我是否正确使用msghdr结构? 因为在上面我提到的例子中,他们使用msg_accrightsmsg_accrightslen ,当我使用它们时我得到了一些错误,所以我不得不使用msg_namemsg_namelen来代替。

Solutions Collecting From Web of "如何使用sendmsg()通过两个进程之间的套接字发送文件描述符?"

这是很难得到正确的。 我建议只使用一个库来为你做。 其中一个最简单的是libancillary 。 它提供了两个函数,一个通过UNIX域套接字发送文件描述符,另一个接收文件描述符。 他们荒谬的简单使用。

问题是您正在将文件描述符传递给msg_name字段。 这是一个地址字段,并不打算传递任意数据。

实际上,文件描述符应该以特殊方式传递,以便内核可以复制接收过程的文件描述符(也许描述符在复制之后会有另一个值)。 这就是为什么有一个特殊的辅助消息类型(SCM_RIGHTS)来传递文件描述符。

以下将工作(我省略了一些错误处理)。 在客户端:

 memset(&child_msg, 0, sizeof(child_msg)); char cmsgbuf[CMSG_SPACE(sizeof(int))]; child_msg.msg_control = cmsgbuf; // make place for the ancillary message to be received child_msg.msg_controllen = sizeof(cmsgbuf); printf("Waiting on recvmsg\n"); rc = recvmsg(worker_sd, &child_msg, 0); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&child_msg); if (cmsg == NULL || cmsg -> cmsg_type != SCM_RIGHTS) { printf("The first control structure contains no file descriptor.\n"); exit(0); } memcpy(&pass_sd, CMSG_DATA(cmsg), sizeof(pass_sd)); printf("Received descriptor = %d\n", pass_sd); 

在服务器中:

 memset(&parent_msg, 0, sizeof(parent_msg)); struct cmsghdr *cmsg; char cmsgbuf[CMSG_SPACE(sizeof(accepted_socket_fd))]; parent_msg.msg_control = cmsgbuf; parent_msg.msg_controllen = sizeof(cmsgbuf); // necessary for CMSG_FIRSTHDR to return the correct value cmsg = CMSG_FIRSTHDR(&parent_msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(accepted_socket_fd)); memcpy(CMSG_DATA(cmsg), &accepted_socket_fd, sizeof(accepted_socket_fd)); parent_msg.msg_controllen = cmsg->cmsg_len; // total size of all control blocks if((sendmsg(server_sd, &parent_msg, 0)) < 0) { perror("sendmsg()"); exit(EXIT_FAILURE); } 

还有man 3 cmsg ,还有一些例子。

您不能通过AF_INET发送文件描述符。 使用一个UNIX域套接字。