sendmsg在发送文件描述符时不工作

时间:2012-07-14 07:07:10

标签: c sockets unix-socket

我使用unix域套接字在不同进程之间发送打开文件描述符。 Unix域套接字工作正常,但当我使用sendmsg发送文件描述符时,发生了一些奇怪的事情。该函数在sendmsg之后返回,sendmsg没有返回任何内容。然后recvmsg给出Invalid Argument错误。这是我的代码。

客户端(发送文件描述符):

#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>


#define CONTROLLEN  CMSG_LEN(sizeof(int))
static struct cmsghdr   *cmptr = NULL;  /* malloc'ed first time */

int send_err(int fd, int errcode, const char *msg);
int send_fd(int fd, int fd_to_send);

int main(int argc, char const *argv[])
{
    int fd_to_send;
    if((fd_to_send = open("vi",O_RDONLY)) < 0)
        printf("vi open failed");

    struct sockaddr_un address;
    int  socket_fd, nbytes;
    char buffer[256];

    socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if(socket_fd < 0)
    {
        printf("socket() failed\n");
        return 1;
    }

    /* start with a clean address structure */
    memset(&address, 0, sizeof(struct sockaddr_un));

    address.sun_family = AF_UNIX;
    snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");

    if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
    {
        printf("connect() failed\n");
        return 1;
    }

    nbytes = snprintf(buffer, 256, "hello from a client");
    write(socket_fd, buffer, nbytes);

    nbytes = read(socket_fd, buffer, 256);
    buffer[nbytes] = 0;

    printf("MESSAGE FROM SERVER: %s\n", buffer);

    //sending the file descriptor    
    printf("From send_fd %d \n",send_fd(socket_fd,fd_to_send));

    close(socket_fd);

    exit(0);

}

int send_err(int fd, int errcode, const char *msg)
{
    int     n;

    if ((n = strlen(msg)) > 0)
        if (write(fd, msg, n) != n)    /* send the error message */
            return(-1);

    if (errcode >= 0)
        errcode = -1;   /* must be negative */

    if (send_fd(fd, errcode) < 0)
        return(-1);

    return(0);
}

int send_fd(int fd, int fd_to_send)
{

    int temp;
    struct iovec    iov[1];
    struct msghdr   msg;
    char            buf[2]; /* send_fd()/recv_fd() 2-byte protocol */

    iov[0].iov_base = buf;
    iov[0].iov_len  = 2;
    msg.msg_iov     = iov;
    msg.msg_iovlen  = 1;
    msg.msg_name    = NULL;
    msg.msg_namelen = 0;
    if (fd_to_send < 0) {
        msg.msg_control    = NULL;
        msg.msg_controllen = 0;
        buf[1] = -fd_to_send;   /* nonzero status means error */
        if (buf[1] == 0)
            buf[1] = 1; /* -256, etc. would screw up protocol */
    } else {
        if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
            return(-1);
        cmptr->cmsg_level  = SOL_SOCKET;
        cmptr->cmsg_type   = SCM_RIGHTS;
        cmptr->cmsg_len    = CONTROLLEN;
        msg.msg_control    = cmptr;
        msg.msg_controllen = CONTROLLEN;
        *(int *)CMSG_DATA(cmptr) = fd_to_send;     /* the fd to pass */
        buf[1] = 0;          /* zero status means OK */
    }
    buf[0] = 0;              /* null byte flag to recv_fd() */
    printf("before sendmsg \n");
    if (temp = sendmsg(fd, &msg, 0) != 2)
    {
        printf("inside sendmsg condition %d\n",temp);
        return(-1);  
    }
    printf("after sendmsg %d\n",temp);
    return(0);

}

服务器(收回文件描述符):

#include <stropts.h>
#include "accesories.c"
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>

#define MAXLINE 10
/* size of control buffer to send/recv one file descriptor */
#define CONTROLLEN  CMSG_LEN(sizeof(int))

static struct cmsghdr   *cmptr = NULL;      /* malloc'ed first time */

int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t));
ssize_t errcheckfunc(int a,const void *b, size_t c);
int connection_handler(int connection_fd);

int main(int argc, char const *argv[])
{
  struct sockaddr_un address;
  int socket_fd, connection_fd;
  socklen_t address_length;
  pid_t child;

  socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
  if(socket_fd < 0)
  {
    printf("socket() failed\n");
    return 1;
  } 

  unlink("./demo_socket");

  /* start with a clean address structure */
  memset(&address, 0, sizeof(struct sockaddr_un));

  address.sun_family = AF_UNIX;
  snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");

  if(bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
  {
    printf("bind() failed\n");
    return 1;
  }

  if(listen(socket_fd, 5) != 0)
  {
    printf("listen() failed\n");
    return 1;
  }

  while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -1)
  {
    connection_handler(connection_fd);

    int fd_to_recv;
    fd_to_recv = recv_fd(socket_fd,&errcheckfunc);

    if(read(fd_to_recv,msgbuf,5) < 0)
      printf("message read failed");
    printf("message received:%s\n",msgbuf);

    close(connection_fd);
  }

  close(socket_fd);
  unlink("./demo_socket");

  return 0;
}

int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t))
{
  int             newfd, nr, status;
  char            *ptr;
  char            buf[MAXLINE];
  struct iovec    iov[1];
  struct msghdr   msg;

  status = -1;
  for ( ; ; ) 
  {
    iov[0].iov_base = buf;
    iov[0].iov_len  = sizeof(buf);
    msg.msg_iov     = iov;
    msg.msg_iovlen  = 1;
    msg.msg_name    = NULL;
    msg.msg_namelen = 0;
    if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
      return(-1);
    msg.msg_control    = cmptr;
    msg.msg_controllen = CONTROLLEN;
    if ((nr = recvmsg(fd, &msg, 0)) < 0) 
    {
      printf("recvmsg errrrror %d %d %s\n",nr,errno,strerror(errno));
      //perror("recvmsg errrrror");
    } else if (nr == 0) 
    {
      perror("connection closed by server");
      return(-1);
    }
    /*
    * See if this is the final data with null & status.  Null
    * is next to last byte of buffer; status byte is last byte.
    * Zero status means there is a file descriptor to receive.
    */
    for (ptr = buf; ptr < &buf[nr]; ) 
    {
      if (*ptr++ == 0) 
      {
        if (ptr != &buf[nr-1])
          perror("message format error");
        status = *ptr & 0xFF;  /* prevent sign extension */
        if (status == 0) 
        {
          if (msg.msg_controllen != CONTROLLEN)
          perror("status = 0 but no fd");
          newfd = *(int *)CMSG_DATA(cmptr);
        } else 
        {
          newfd = -status;
        }
        nr -= 2;
      }
    }
    if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
      return(-1);
    if (status >= 0)    /* final data has arrived */
      return(newfd);  /* descriptor, or -status */
  }
}

ssize_t errcheckfunc(int a,const void *b, size_t c)
{
    return 0;
}

int connection_handler(int connection_fd)
{
  int nbytes;
  char buffer[256];

  nbytes = read(connection_fd, buffer, 256);
  buffer[nbytes] = 0;

  printf("MESSAGE FROM CLIENT: %s\n", buffer);
  nbytes = snprintf(buffer, 256, "hello from the server");
  write(connection_fd, buffer, nbytes);


  return 0;
}

客户输出:

MESSAGE FROM SERVER: hello from the server
before sendmsg 

甚至不打印inside sendmsg conditionafter sendmsg

1 个答案:

答案 0 :(得分:2)

由于对mark a comment as an answer的功能请求仍然被拒绝,我在此处复制上述解决方案。

您对recvmsg()的调用是尝试从侦听套接字而不是连接的套接字接收数据,因为您的主循环正在将socket_fd传递给recv_fd()而不是connection_fd。 - Matthew Slattery