2012-07-14 25 views
0

我正在使用unix域套接字在不同進程之間發送打開的文件描述符。 Unix域套接字工作正常,但當我用sendmsg發送文件描述符發生了一些奇怪的事情。該函數僅在sendmsg之後返回,sendmsg不返回任何內容。然後recvmsg給出Invalid Argument錯誤。這是我的代碼。Sendmsg在發送文件描述符時不起作用

客戶端(發送文件描述符):

#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); 

} 

服務器(recv的文件描述符):

MESSAGE FROM SERVER: hello from the server 
before sendmsg 

不:

#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; 
} 

客戶端的輸出甚至inside sendmsg conditionafter sendmsg打印?

+2

connection_handler關閉你的管道。 – Mat 2012-07-14 07:56:53

+0

謝謝!但它仍然不起作用。現在客戶端正常工作,但是在服務器recvmsg中給出'INVALID Argument error' – 2012-07-14 09:43:57

+1

當你在'strace'下運行程序時,這種問題的原因常常變得明顯。 – zwol 2012-07-14 10:21:24

回答

2

由於對mark a comment as an answer的功能請求仍然下降,所以我在此複製上述解決方案。

你調用recvmsg()正試圖從一個監聽套接字,而不是一個連接套接字接收數據,因爲你的主循環傳遞socket_fd到recv_fd(),而不是connection_fd。 - Matthew Slattery