现在的位置: 首页 > 综合 > 正文

11.1.6 传递文件描述符的例子(2)

2013年01月19日 ⁄ 综合 ⁄ 共 5094字 ⁄ 字号 评论关闭

2.进程B的代码

进程B获得进程A中发送过来的消息,并从中取得文件描述符。根据获得的文件描述符,直接从文件中读取数据,并将数据在标准输出打印出来。

  1. 001     #include <sys/types.h> 
  2. 002     #include <sys/socket.h> 
  3. 003     #include <Linux/un.h> 
  4. 004     #include <string.h> 
  5. 005     #include <signal.h> 
  6. 006     #include <stdio.h> 
  7. 007     #include <errno.h> 
  8. 008     #include <unistd.h> 
  9. 009       
  10. 010     /*  
  11. 011     *   从fd中接收消息,并将文件描述符放在指针recvfd中  
  12. 012     */  
  13. 013     ssize_t recv_fd(int fd, void*data, size_t bytes, int*recvfd)  
  14. 014     {  
  15. 015         struct msghdr msghdr_recv;                  /*接收消息接收*/  
  16. 016         struct iovec iov[1];                        /*接收数据的向量*/  
  17. 017         size_t n;  
  18. 018         int newfd;  
  19. 019           
  20. 020         union{  
  21. 021             struct cmsghdr cm;  
  22. 022             char control[CMSG_SPACE(sizeof(int))];    
  23. 023         }control_un;  
  24. 024         struct cmsghdr*pcmsghdr;                        /*消息头部*/  
  25. 025         msghdr_recv.msg_control = control_un.control;   /*控制消息*/  
  26. 026         msghdr_recv.msg_controllen = sizeof(control_un.control);      
  27.                                                         /*控制消息的长度*/  
  28. 027           
  29. 028         msghdr_recv.msg_name = NULL;    /*消息的名称为空*/  
  30. 029         msghdr_recv.msg_namelen = 0;    /*消息的长度为空*/  
  31. 030           
  32. 031         iov[0].iov_base = data;         /*向量的数据为传入的数据*/  
  33. 032         iov[0].iov_len = bytes;         /*向量的长度为传入数据的长度*/  
  34. 033         msghdr_recv.msg_iov = iov;      /*消息向量指针*/  
  35. 034         msghdr_recv.msg_iovlen = 1;     /*消息向量的个数为1个*/  
  36. 035         if((n = recvmsg(fd, &msghdr_recv, 0))<=0)   /*接收消息*/  
  37. 036             return n;  
  38. 037               
  39. 038         if((pcmsghdr = CMSG_FIRSTHDR(&msghdr_recv))!= NULL &&  
  40.                                             /*获得消息的头部*/  
  41. 039             pcmsghdr->cmsg_len == CMSG_LEN(sizeof(int))){     
  42.                                             /*获得消息的长度为int*/  
  43. 040             if(pcmsghdr->cmsg_level != SOL_SOCKET)  
  44.                                             /*消息的level应该为SOL_SOCKET*/  
  45. 041                 printf("control level != SOL_SOCKET/n");  
  46. 042               
  47. 043             if(pcmsghdr->cmsg_type != SCM_RIGHTS)   /*消息的类型判断*/  
  48. 044                 printf("control type != SCM_RIGHTS/n");  
  49. 045                   
  50. 046                 *recvfd =*((int*)CMSG_DATA(pcmsghdr));  
  51.                                             /*获得打开文件的描述符*/  
  52. 047         }else  
  53. 048             *recvfd = -1;  
  54. 049               
  55. 050         return n;                       /*返回接收消息的长度*/  
  56. 051     }  
  57. 052       
  58. 053     int my_open(const char*pathname, int mode)  
  59. 054     {  
  60. 055         int fd, sockfd[2],status;  
  61. 056         pid_t childpid;  
  62. 057         char c, argsockfd[10],argmode[10];  
  63. 058           
  64. 059         socketpair(AF_LOCAL,SOCK_STREAM,0,sockfd);  /*建立socket*/  
  65. 060         if((childpid = fork())==0){                 /*子进程*/  
  66. 061             close(sockfd[0]);                       /*关闭sockfd[0]*/  
  67. 062             snprintf(argsockfd, sizeof(argsockfd),"%d",sockfd[1]);               
    /*socket描述符*/  
  68. 063             snprintf(argmode, sizeof(argmode),"%d",mode);  
  69.                                                         /*打开文件的方式*/  
  70. 064             execl("./openfile","openfile",argsockfd, pathname,   
  71.                 argmode,(char*)NULL)                    ;/*执行进程A*/  
  72. 065             printf("execl error/n");  
  73. 066         }     
  74. 067         /*父进程*/  
  75. 068         close(sockfd[1]);  
  76. 069         /*等待子进程结束*/  
  77. 070         waitpid(childpid, &status,0);  
  78. 071           
  79. 072         if(WIFEXITED(status)==0){               /*判断子进程是否结束*/  
  80. 073             printf("child did not terminate/n") ;  
  81. 074         if((status = WEXITSTATUS(status))==0){  /*子进程结束*/  
  82. 075             recv_fd(sockfd[0],&c,1,&fd);    /*接收进程A打开的文件描述符*/  
  83. 076         }   else{  
  84. 077             errno = status;  
  85. 078             fd = -1;      
  86. 079         }     
  87. 080           
  88. 081         close(sockfd[0]);                   /*关闭sockfd[0]*/  
  89. 082         return fd;                          /*返回进程A打开文件的描述符*/  
  90. 083       
  91. 084         }  
  92. 085     }  
  93. 086       
  94. 087     #define BUFFSIZE 256                    /*接收的缓冲区大小*/  
  95. 088     int main(int argc, char*argv[])  
  96. 089     {  
  97. 090         int fd, n;  
  98. 091         char buff[BUFFSIZE];                /*接收缓冲区*/  
  99. 092           
  100. 093         if(argc !=2)  
  101. 094             printf("error argc/n");  
  102. 095               
  103. 096         if((fd = my_open(argv[1], O_RDONLY))<0)  
  104.                                                 /*获得进程A打开的文件描述符*/  
  105. 097             printf("can't open %s/n",argv[1]);  
  106. 098               
  107. 099         while((n = read(fd, buff, BUFFSIZE))>0) /*读取数据*/  
  108. 100         write(1,buff,n);                            /*写入标准输出*/  
  109. 101           
  110. 102         return(0);    
  111. 103     } 

分为如下的步骤:

第013~051行为函数recv_fd(),它从fd接收消息,并返回获得消息中的信息:打开文件的描述符。

第015行建立一个消息,之后填充此消息的成员数据,并发送给fd。

第016行为向量,消息的数据在此项中保存。

第020~023行建立一个联合结构,便于进行消息的处理。

第025行填充消息的控制部分,第026行为控制部分的长度。

第028行和第029行用于将消息的名称置空。

第031行和第032行将传入的数据和长度传递给向量成员。

第033行将向量填充给消息,第034行设置向量的个数。

第035行接收消息。

第038行取得消息的第一个头部。

第039行判断消息长度是否为int长度。

第040行判断消息level是否为SOL_SOCKET。

第043行判断消息的类型是否为SCM_RIGHTS。

第046行获得传入的文件描述符。

第053~085行为my_open()函数,按照传入的路径和模式打开文件。

第059行调用socketpair()函数获得socket对。

第060行为fork()进程

第061~066行在子进程中调用外部进程打开文件。

第069~084行为父进程处理过程。等待子进程处理函数的结束,并接收传过来的值。

第087~103行为主函数。它调用my_open()获得进程A传入的文件描述符,从文件中读取数据并显示到标准输出。

抱歉!评论已关闭.