2010-12-20 44 views
11

我正在使用UNIX域套接字将文件描述符传输到另一个进程。这工作正常,但是当我第一次尝试查看套接字是否使用select()写入时,sendmsg()调用失败,并显示错误文件描述符错误。通过UNIX域套接字发送文件描述符,并选择()

如果我没有将文件描述符信息添加到msghdr结构中,sendmsg()函数与select()结合使用可以正常工作,因此冲突似乎在select()和传输文件描述符之间。

我在select(),recvmsg()或任何其他手册页上找不到任何有关此信息。由于这需要成为一个将文件描述符分配给多个进程的服务器,我仍然希望能够使用select()。

有什么我可以做的这项工作,或者是否有人知道替代解决方案?

平台是Ubuntu 10.4。

这是初始化结构的代码:



struct cmsghdr_fd : public cmsghdr 
{ 
    int fd; 
}; 

int sendfd(int sock, int fd) 
{ 
    struct msghdr hdr; 
    struct iovec data; 
    struct cmsghdr_fd msgdata; 

    char dummy = '*'; 
    data.iov_base = &dummy; 
    data.iov_len = sizeof(dummy); 

    hdr.msg_name = NULL; 
    hdr.msg_namelen = 0; 
    hdr.msg_iov = &data; 
    hdr.msg_iovlen = 1; 
    hdr.msg_flags = 0; 

    hdr.msg_control = &msgdata; 
    hdr.msg_controllen = sizeof(msgdata); 

    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); 
    cmsg->cmsg_len = hdr.msg_controllen; 
    cmsg->cmsg_level = SOL_SOCKET; 
    cmsg->cmsg_type = SCM_RIGHTS; 

    *(int*)CMSG_DATA(cmsg) = fd; 

    int n = sendmsg(sock, &hdr, 0); 

    if(n == -1) 
    printf("sendmsg() failed: %s (socket fd = %d)\n", strerror(errno), sock); 

    return n; 
} 

同样,这个工作,只要我不叫选择()首先检查插座是否准备好写。

+0

你可以包含你的代码来填充'msghdr'和'cmsghdr'吗? – 2010-12-20 13:52:32

+0

我想验证你的周围的代码不会破坏msghdr的长度(或数据本身),因为从很短的内核处理读取,我认为这将是可能的问题 – Hasturkun 2010-12-20 13:55:19

+0

我添加了填充结构的问题。 – svdree 2010-12-20 14:10:05

回答

10

我尝试了this page的sendfd代码,它由nos友善地提供,虽然它只是略有不同,但它在与select()组合使用时仍然有效。这就是代码现在的样子:



    int sendfd(int sock, int fd) 
    { 
     struct msghdr hdr; 
     struct iovec data; 

     char cmsgbuf[CMSG_SPACE(sizeof(int))]; 

     char dummy = '*'; 
     data.iov_base = &dummy; 
     data.iov_len = sizeof(dummy); 

     memset(&hdr, 0, sizeof(hdr)); 
     hdr.msg_name = NULL; 
     hdr.msg_namelen = 0; 
     hdr.msg_iov = &data; 
     hdr.msg_iovlen = 1; 
     hdr.msg_flags = 0; 

     hdr.msg_control = cmsgbuf; 
     hdr.msg_controllen = CMSG_LEN(sizeof(int)); 

     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); 
     cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 
     cmsg->cmsg_level = SOL_SOCKET; 
     cmsg->cmsg_type = SCM_RIGHTS; 

     *(int*)CMSG_DATA(cmsg) = fd; 

     int n = sendmsg(sock, &hdr, 0); 

     if(n == -1) 
     printf("sendmsg() failed: %s (socket fd = %d)\n", strerror(errno), sock); 

     return n; 
     } 

+0

不要再使用'select'。我在对OP的评论中给出了理由。使用'poll'系统​​调用(可移植到BSD)或'epoll'系统​​。 – Omnifarious 2017-12-05 01:37:33