LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
12
返回列表 发新帖
楼主: jadywang

套接字描述符如何传给子进程?

[复制链接]
发表于 2005-1-1 22:08:34 | 显示全部楼层
我想楼主的目的就是这个吧。
发表于 2005-1-2 00:58:36 | 显示全部楼层

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <arpa/inet.h>
  5. #include <netinet/in.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>

  9. void err_quit(const char *msg)
  10. {
  11.         printf("%s\n", msg);
  12.         exit(0);
  13. }

  14. ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd)
  15. {
  16.         struct msghdr        msg;
  17.         struct iovec        iov[1];

  18. #ifdef        HAVE_MSGHDR_MSG_CONTROL
  19.         union {
  20.           struct cmsghdr        cm;
  21.           char                                control[CMSG_SPACE(sizeof(int))];
  22.         } control_un;
  23.         struct cmsghdr        *cmptr;

  24.         msg.msg_control = control_un.control;
  25.         msg.msg_controllen = sizeof(control_un.control);

  26.         cmptr = CMSG_FIRSTHDR(&msg);
  27.         cmptr->cmsg_len = CMSG_LEN(sizeof(int));
  28.         cmptr->cmsg_level = SOL_SOCKET;
  29.         cmptr->cmsg_type = SCM_RIGHTS;
  30.         *((int *) CMSG_DATA(cmptr)) = sendfd;
  31. #else
  32.         msg.msg_accrights = (caddr_t) &sendfd;
  33.         msg.msg_accrightslen = sizeof(int);
  34. #endif

  35.         msg.msg_name = NULL;
  36.         msg.msg_namelen = 0;

  37.         iov[0].iov_base = ptr;
  38.         iov[0].iov_len = nbytes;
  39.         msg.msg_iov = iov;
  40.         msg.msg_iovlen = 1;

  41.         return(sendmsg(fd, &msg, 0));
  42. }

  43. ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
  44. {
  45.         struct msghdr        msg;
  46.         struct iovec        iov[1];
  47.         ssize_t                        n;

  48. #ifdef        HAVE_MSGHDR_MSG_CONTROL
  49.         union {
  50.           struct cmsghdr        cm;
  51.           char                                control[CMSG_SPACE(sizeof(int))];
  52.         } control_un;
  53.         struct cmsghdr        *cmptr;

  54.         msg.msg_control = control_un.control;
  55.         msg.msg_controllen = sizeof(control_un.control);
  56. #else
  57.         int                                newfd;

  58.         msg.msg_accrights = (caddr_t) &newfd;
  59.         msg.msg_accrightslen = sizeof(int);
  60. #endif

  61.         msg.msg_name = NULL;
  62.         msg.msg_namelen = 0;

  63.         iov[0].iov_base = ptr;
  64.         iov[0].iov_len = nbytes;
  65.         msg.msg_iov = iov;
  66.         msg.msg_iovlen = 1;

  67.         if ( (n = recvmsg(fd, &msg, 0)) <= 0)
  68.                 return(n);

  69. #ifdef        HAVE_MSGHDR_MSG_CONTROL
  70.         if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
  71.             cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
  72.                 if (cmptr->cmsg_level != SOL_SOCKET)
  73.                         err_quit("control level != SOL_SOCKET");
  74.                 if (cmptr->cmsg_type != SCM_RIGHTS)
  75.                         err_quit("control type != SCM_RIGHTS");
  76.                 *recvfd = *((int *) CMSG_DATA(cmptr));
  77.         } else
  78.                 *recvfd = -1;                /* descriptor was not passed */
  79. #else
  80. /* *INDENT-OFF* */
  81.         if (msg.msg_accrightslen == sizeof(int))
  82.                 *recvfd = newfd;
  83.         else
  84.                 *recvfd = -1;                /* descriptor was not passed */
  85. /* *INDENT-ON* */
  86. #endif

  87.         return(n);
  88. }

  89. void child(int fd, int sockfd[2])
  90. {
  91.         //close(sockfd[0]);
  92.         close(fd);
  93.        
  94.         printf("Child %ld starting\n", getpid());
  95.         char msg[] = "Hello world!\n";
  96.         for (;;) {
  97.                 int conn = 0;
  98.                 char c = 0;
  99.                 ssize_t size = read_fd(sockfd[1], &c, 1, &conn);
  100.                 printf("[%d] Get connection %d\n", getpid(), conn);
  101.                 if (size == -1)
  102.                 {
  103.                         printf("[%d] Failed to read\n", getpid());
  104.                         continue;
  105.                 }
  106.                 printf("[%d] Write msg: %s\n", getpid(), msg);
  107.                 if (write(conn, msg, sizeof(msg)) == -1)
  108.                         printf("[%d] Failed to write\n", getpid());
  109.                 close(conn);   
  110.         }
  111. }

  112. void parent(int fd, int sockfd[2])
  113. {
  114.         //close(sockfd[1]);

  115.         int conn = 0;
  116.         sockaddr_in addr;
  117.         socklen_t size = sizeof(addr);
  118.         while ((conn = accept(fd, (sockaddr*)&addr, &size)) != -1)
  119.         {
  120.                 char c = '\0';
  121.                 printf("[%d] Receive connection %d\n", getpid(), conn);
  122.                 write_fd(sockfd[0], &c, 1, conn);
  123.                 close(conn);
  124.         }
  125. }

  126. int main(int argc, char *argv[])
  127. {
  128.         if (argc != 2)
  129.         {
  130.                 printf("Usage: %s <port>\n", argv[0]);
  131.                 return -1;
  132.         }
  133.         int port = atoi(argv[1]);
  134.         if (port == 0)
  135.         {
  136.                 printf("Wrong port!\n");
  137.                 return -1;
  138.         }
  139.        
  140.         int fd = socket(AF_INET, SOCK_STREAM, 0);
  141.         if (fd == -1)
  142.         {
  143.                 printf("Failed to create socket!\n");
  144.                 return -1;
  145.         }
  146.        
  147.         sockaddr_in addr;
  148.         memset(&addr, 0, sizeof(addr));
  149.         addr.sin_family = AF_INET;
  150.         addr.sin_addr.s_addr = htonl(INADDR_ANY);
  151.         addr.sin_port = htons(port);
  152.         bind(fd, (sockaddr*)&addr, sizeof(addr));
  153.         listen(fd, 5);

  154.         int sockfd[2] = {0};
  155.         socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
  156.         pid_t pid = fork();
  157.         if (pid == 0)
  158.         {
  159.                 child(fd, sockfd);
  160.         }
  161.         else
  162.         {
  163.                 parent(fd, sockfd);
  164.         }
  165.         return 0;
  166. }
复制代码

使用:
$./child 6669
测试:
$telnet 127.0.0.1 6669
发表于 2005-1-2 10:09:53 | 显示全部楼层
楼上的这段代码不错,能很好的说明问题。从运行结果可以明显看出,父子进程对应同一个系统文件表项的文件描述符(或套接字)(在通常情况下)是不一样的。文件描述符毕竟是进程私有的资源,不会让其他进程共享。但每个文件描述符(或套接字)实际上都是对内核文件表的引用,这个内核文件表是由所有进程共享的。一个进程多以多次引用同一个文件表项,如用dup函数复制文件描述符。也可以把一个对文件表项的引用传递给其它进程,就象楼上所做的那样。但真正的文件描述符是一个进程打开的文件数组的索引,这个索引对于拥有文件描述符的进程才有意义。把它直接传递给其他进程是行不通的。对于这一点,我认为《APUE》上的标题表达不够准确,让人以为把一个进程的文件描述符象数据一样直接传递给另一个进程就可以使用了,实际上是行不通的(但是《APUE》的内容确实说清楚了),楼主写的程序很明显就是理解成那样了。
 楼主| 发表于 2005-1-5 22:00:57 | 显示全部楼层
呵呵,这两天考试,没来看,想不到两位给了这么详细的东西,谢谢先!
研究一下再说!……
 楼主| 发表于 2005-1-5 23:01:25 | 显示全部楼层
明白了,上面的代码用sendmsg()和recvmsg()实现了类似write()和read()的能读写描述夫的write_fd()和read_fd()。
BSD中,只有通过sendmsg和recvmsg才能在子进程的描述表里建立一个与被传送描述符指向同一个文件表项的描述符。
谢谢两位!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表