C -Ubuntu Linux发送带有UNIX域套接字的文件描述符



我正在尝试在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;

最新更新