C 如何使用sendmsg()通过两个进程之间的套接字发送文件描述符?
在@cnicutar回答我这个问题后,我尝试将一个文件描述符从父进程发送到其子进程。基于此,我编写了以下代码:C 如何使用sendmsg()通过两个进程之间的套接字发送文件描述符?,c,linux,sockets,network-programming,file-descriptor,C,Linux,Sockets,Network Programming,File Descriptor,在@cnicutar回答我这个问题后,我尝试将一个文件描述符从父进程发送到其子进程。基于此,我编写了以下代码: 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; /* =======================================
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);
}
}
我应该如何解决此问题?我是否正确使用了
msghdr
结构?因为在我上面提到的例子中,他们使用了msg_accrights
和msg_accrightslen
,我在使用它们时遇到了一些错误,所以我不得不使用msg_name
和msg_namelen
。这是非常难以正确的。我建议你使用一个为你做这件事的图书馆。其中一个最简单的例子是。它提供两个函数,一个用于通过UNIX域套接字发送文件描述符,另一个用于接收文件描述符。它们的使用极其简单。您无法通过AF\u INET发送文件描述符。使用UNIX域套接字。问题是您正在msg\u name
字段中传递文件描述符。这是一个地址字段,不用于传递任意数据
事实上,应该以一种特殊的方式传递文件描述符,以便内核可以为接收进程复制文件描述符(复制之后,描述符可能会有另一个值)。这就是为什么有一种特殊的辅助消息类型(SCM\U权限)来传递文件描述符
下面的方法可以工作(我省略了一些错误处理)。
在客户端:
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);
}
memset(&parent_-msg,0,sizeof(parent_-msg));
结构cmsghdr*cmsg;
char cmsgbuf[CMSG_空间(sizeof(accepted_socket_fd))];
parent_msg.msg_control=cmsgbuf;
parent_msg.msg_controllen=sizeof(cmsgbuf);//CMSG_FIRSTHDR必须返回正确的值
cmsg=cmsg_FIRSTHDR(&parent_msg);
cmsg->cmsg\U电平=SOL\U插座;
cmsg->cmsg\U类型=SCM\U权限;
cmsg->cmsg_len=cmsg_len(sizeof(已接受的插座fd));
memcpy(CMSG_数据(CMSG)和接受的_套接字_fd,sizeof(接受的_套接字_fd));
父项\u msg.msg\u controllen=cmsg->cmsg\u len;//所有控制块的总大小
if((sendmsg(server\u sd,&parent\u msg,0))<0)
{
perror(“sendmsg()”);
退出(退出失败);
}
另请参见
man 3 cmsg
,其中有一些示例。用户不是使用AF_INET进行套接字传递,而是使用socketpair创建的AF_UNIX套接字。发送消息后,服务器中fd的状态如何?它是打开的,需要关闭,还是sendmsg/cmsg关闭?在示例代码中,我从未看到服务器关闭fd,如果sendmsg不关闭它,这是必要的。我相信它会保持打开状态,如果您不先关闭,当进程终止时,操作系统会关闭它。但我还没有完全检查过。
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);
}