2010-12-20 77 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