linux进程线程间通信:传递文件描述符

问题描述

主进程中创建一个子进程和两个线程,每个线程负责accept连接,用socketpair创建了通信管道,在主进程中把服务端socket及管道的其中一个套接字传递给了线程,本来想在线程中把客户端socket文件描述符通过sendmsg函数传递给子进程,但是提示无效的文件描述符。

相关代码

struct mypara
{
    int tcp_sock;//参数1
    int pip_sock;//参数2
};
int main(int argc, char const *argv[])
{
    int serv_sock;
    struct sockaddr_in serv_adr;
    pthread_t t_id[NUM_THREAD];
    int j,k,state,str_len,fd,rv;
    pid_t pid;
    int fds[2];
    struct sigaction act;
    struct mypara pstru;
    char buf[BUF_SIZE];


    if(argc!=2)
    {
        printf("Usage: %s <port>\n", argv[0]);
        exit(1);
    }

    //注册信号处理函数
    act.sa_handler=read_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    state=sigaction(SIGCHLD,&act,0);

    serv_sock=socket(PF_INET,SOCK_STREAM,0);
    memset(&serv_adr,0,sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));

    if(bind(serv_sock,(struct sockaddr *)&serv_adr,sizeof(serv_adr))==-1)
        error_handling("bind()...error");
    if(listen(serv_sock,5)==-1)
        error_handling("listen()...error");
    
    //pipe(fds);  //创建管道
    socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
    if (rv < 0) 
    {
        printf("Call socketpair error, errno is %d\n", errno);
        return errno;
    }
    pid=fork();
    pstru.tcp_sock = serv_sock;
    pstru.pip_sock = fds[1];
    if(pid==0) /*子进程处理逻辑*/
    {
        close(serv_sock);//子进程销毁服务端套接字,因为不需要使用
        puts("work start...");
        while(1)
        {
           
           fd = recv_fd(fds[0]);
           str_len = read(fds[0],buf,BUF_SIZE);//读
           if (str_len > 0)
               puts(buf);
           if (fd > 0){
               write(fd,buf,str_len);
           }
        }
        return 0;
    }
    
    for (j = 0; j < NUM_THREAD; j++)
    {
        pthread_create(&t_id[j],NULL,handle_clnt,(void*)&pstru);
    }

    for(k=0;k<NUM_THREAD;k++)
        pthread_join(t_id[k],NULL);
    
    

    return 0;
}
//每个线程执行的main入口函数
void * handle_clnt(void * arg)
{

    struct mypara *pstru;
    pstru = (struct mypara *) arg;
    int serv_sock = pstru->tcp_sock;  //tcp服务端描述符
    int pip_sock = pstru->pip_sock;   //管道描述符

    
    int clnt_sock;
    struct sockaddr_in clnt_adr;
    socklen_t adr_sz;
    int str_len,i;
    char buf[BUF_SIZE];//接收数据

    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd,event_cnt;


    epfd=epoll_create(EPOLL_SIZE);
    ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);


    event.events=EPOLLIN;
    event.data.fd=serv_sock;
    epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);

    while(1)
    {
        event_cnt=epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
        if(event_cnt==-1)
        {
            puts("epoll_wait() error");
            break;
        }
        puts("return epoll_wait");
        for(i=0;i<event_cnt;i++)
        {
            if(ep_events[i].data.fd==serv_sock)
            {
                adr_sz=sizeof(clnt_adr);
                clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
                event.events=EPOLLIN;
                event.data.fd=clnt_sock;
                epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
                printf("connected client: %d \n", clnt_sock);
            }
            else
            {
                str_len=read(ep_events[i].data.fd,buf,BUF_SIZE);
                if(str_len==0)
                {
                    epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
                    close(ep_events[i].data.fd);
                    printf("closed client: %d \n", ep_events[i].data.fd);
                }
                else
                {   
                    write(pip_sock,buf,str_len);//写
                    send_fd(pip_sock, ep_events[i].data.fd);
                    //write(ep_events[i].data.fd,buf,str_len);
                }
            }
        }
    }
    close(serv_sock);
    close(epfd);
    
    return NULL;
}

void read_childproc(int sig)
{
    pid_t pid;
    int status;

    pid=waitpid(-1,&status,WNOHANG);
    printf("removed proc id: %d \n", pid);
}

void error_handling(char *msg)
{
    fputs(msg,stderr);
    fputc('\n',stderr);
    exit(1);
}

int send_fd(int fd, int fd_to_send)
{
    struct msghdr msg;
    struct iovec iov[1];
    char buf[100];
    union {  //保证cmsghdr和msg_control对齐
        struct cmsghdr cm;
        char control[CMSG_SPACE(sizeof(int))];
    } control_un;
    struct cmsghdr *pcmsg;
    int ret;
 
 
    //udp需要,tcp无视
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    iov[0].iov_base = buf;
    iov[0].iov_len = 100;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    //设置缓冲区和长度
    msg.msg_control = control_un.control;
    msg.msg_controllen = sizeof(control_un.control);
    //直接通过CMSG_FIRSTHDR取得附属数据
    pcmsg = CMSG_FIRSTHDR(&msg);
    pcmsg->cmsg_len = CMSG_LEN(sizeof(int));
    pcmsg->cmsg_level = SOL_SOCKET;
    pcmsg->cmsg_type = SCM_RIGHTS;  //指明发送的是描述符
    *((int*)CMSG_DATA(pcmsg)) == fd_to_send;  //把描述符写入辅助数据
 
    ret = sendmsg(fd, &msg, 0);
    if(ret < 0) {
        printf("sendmsg error, errno is %d\n", errno);
        printf("%s\n",strerror(errno));
        printf ("ret = %d, filedescriptor = %d\n", ret, fd_to_send);
        return errno;
    }

    return 0;
}
 
 
int recv_fd(int fd)
{
    struct msghdr msg;
    struct iovec iov[1];
    char buf[100];
    int ret,recvfd;
 
    union {  //对齐
        struct cmsghdr cm;
        char control[CMSG_SPACE(sizeof(int))];
    }  control_un;
    struct cmsghdr  * pcmsg;



    msg.msg_name  = NULL;
    msg.msg_namelen  = 0;
    //设置数据缓冲区
    iov[0].iov_base = buf;
    iov[0].iov_len = 100;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    //设置辅助数据缓冲区和长度
    msg.msg_control = control_un.control;
    msg.msg_controllen  =  sizeof(control_un.control);
    //接收
    ret = recvmsg(fd , &msg, 0);
    if( ret <= 0 ) {
        return ret;
    }
    //检查是否收到了辅助数据,以及长度
    if((pcmsg = CMSG_FIRSTHDR(&msg) ) != NULL && ( pcmsg->cmsg_len == CMSG_LEN(sizeof(int)))) {
        if ( pcmsg->cmsg_level  !=  SOL_SOCKET ) {
            printf("cmsg_leval is not SOL_SOCKET\n");
            return -1;
        }

        if ( pcmsg->cmsg_type != SCM_RIGHTS ) {
            printf ( "cmsg_type is not SCM_RIGHTS" );
            return -1;
        }
        //这就是我们接收的描述符
        recvfd = *((int*)CMSG_DATA(pcmsg));
        return recvfd;

    }
 
    return -1;
}

错误信息:

sendmsg error, errno is 9
Bad file descriptor
ret = -1, filedescriptor = 8

阅读 2.3k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进