我正在尝试在socketpair之间发送文件描述符,并在下面粘贴的代码。此代码来自:http://www.techdeviancy.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 pairn");
int pid = fork();
if (pid > 0) // in parent
{
fprintf(stderr,"Parent at workn");
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 %sn", filename, strerror(errno));
fprintf(stderr,"FILE TO SEND HAS DESCRIPTOR: %dn",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 exitsn");
}
else // in child
{
fprintf(stderr,"Child at playn");
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: %dn",nbytes);
write(1, buffer, nbytes);
}
printf("Done: %d %s!n",nbytes,strerror(errno));
close(fd);
}
return 0;
}
文件偏移量由两个进程共享。因此,当父母的过程读取直到eof时,孩子的过程没有任何内容。
这与两个流程从父母那里继承文件描述符时相同,例如壳命令:
{ echo first cat; cat ; echo second cat ; cat ; } < filename
第一个cat
命令将读取所有文件,第二个cat
将无需阅读。
引用Richard Stevens(编程Unix网络):
"正常的是,接收过程中的描述符号与发送过程中的描述符编号有所不同。传递描述符不会传递描述符编号,而是在接收过程中创建了一个新的描述符,该描述符指向指向内核中的同一文件条目与传输过程发送的描述符。"
Barmar说是正确的。
我完成了一些代码,使事情正确。试图开始文件:
lseek(fd, 0,SEEK_SET);
代码片段
int fd = recv_fd(sock);
printf("Read %d!n", fd);
lseek(fd, 0,SEEK_SET);
char buffer[256];
ssize_t nbytes;