账号密码登录
微信安全登录
微信扫描二维码登录

登录后绑定QQ、微信即可实现信息互通

手机验证码登录
找回密码返回
邮箱找回 手机找回
注册账号返回
其他登录方式
分享
  • 收藏
    X
    linux进程线程间通信:传递文件描述符
    21
    0

    问题描述

    主进程中创建一个子进程和两个线程,每个线程负责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

    0
    打赏
    收藏
    点击回答
    您的回答被采纳后将获得:提问者悬赏的 11 元积分
        全部回答
    • 0
    • 半夏° 普通会员 1楼

      在Linux中,进程间可以通过多种方式进行通信,包括管道、套接字、文件描述符等。其中,文件描述符是最常用的通信方式之一。

      文件描述符是Linux系统中的一种特殊文件类型,它提供了一种用于在进程间共享数据的机制。通过文件描述符,进程可以在文件描述符上读写数据,从而实现进程间的通信。

      以下是使用文件描述符进行进程间通信的一般步骤:

      1. 创建文件描述符:在进行通信之前,首先需要创建一个文件描述符。这通常涉及到在文件中写入数据,然后使用open()函数打开这个文件描述符。

      2. 操作文件描述符:在进行通信的过程中,可以使用文件描述符来读取数据或写入数据。例如,可以使用read()函数从文件描述符中读取数据,或者使用write()函数向文件描述符中写入数据。

      3. 关闭文件描述符:在完成通信后,需要关闭文件描述符。这通常涉及到使用close()函数关闭文件描述符。

      以下是一个简单的示例,展示了如何使用文件描述符进行进程间通信:

      ```c

      include

      include

      include

      include

      include

      include

      int main(int argc, char argv[]) { char file_desc = "/tmp/test.txt"; int fd = open(file_desc, O_RDWR);

      if (fd < 0) {
          perror("Failed to open file");
          return 1;
      }
      
      char data[1024];
      write(fd, "Hello, World!", sizeof(data));
      printf("Data written: %s\n", data);
      
      close(fd);
      
      return 0;
      

      } ```

      在这个示例中,我们首先创建了一个文件描述符/tmp/test.txt,然后使用open()函数打开了这个文件描述符。然后,我们使用write()函数向文件描述符中写入了一个字符串"Hello, World!"。最后,我们关闭了文件描述符。

    更多回答
    扫一扫访问手机版
    • 回到顶部
    • 回到顶部