【问题标题】:Close opened sockets in child process关闭子进程中打开的套接字
【发布时间】:2013-10-19 14:34:42
【问题描述】:

我有一个 SIP 服务器(守护进程),它正在侦听 tcp 套接字 5060。现在在这个父进程中,我创建了一个子进程并在子进程中做一些事情。 现在,当我在父进程中关闭此 tcp 套接字并尝试再次创建(假设我在此服务器上禁用和启用 SIP)时,会发生什么,创建此套接字会给我错误。我已经调试了这个问题并找到了根本原因。根本原因是在创建子节点时,它从父节点继承(获取副本)所有打开的 fd/套接字。当父级关闭 tcp 套接字时,它仍然在子级(ref_counter!=0)中打开,因此我无法在父级中再次打开套接字!

现在,我想要的通用解决方案是 - 一旦启动子进程,它就会检查任何打开的 fd(IPv4/TCP 类型)并关闭它们,这样子进程就不会对父进程产生副作用。这如何在 C-unix 中完成? 我已经考虑过按照 system(lsof | grep | awk) 的方向进行操作并获取文件描述符,但是我该如何关闭它们呢? 任何其他解决方案来关闭孩子的套接字?有没有一种方法可以传递端口号,它给了我已经创建的 fd ?

我不想要的解决方案是(这对我没有帮助)-
1.在父进程中,最初在创建带有一些标志的tcp套接字时,它们不会被子进程复制。 (我无法修改父级中的套接字创建)! 2. 在创建子进程时将文件描述符从父进程传递给子进程。我不能那样做,因为我没有那个 fd。解决方案必须是需要放在子进程中的东西!

谢谢

【问题讨论】:

  • 如果不编辑父进程代码,我不确定这是否可行。每当我遇到这个问题时,我都会使用您不想要的解决方案 2。
  • 一种可能的方法,如果使用 Linux:检查 /proc//fd/ 中的链接。关闭链接到 'socket:[inode]' 的描述符。
  • 我怀疑您误诊了根本原因,现在正在寻找两种不受欢迎的解决方案来解决您自己制造的问题。孩子们首先关闭监听套接字,而父母在分叉后立即关闭数据套接字应该是微不足道的。
  • @MichaelBrennan - 谢谢。如果这些链接(它们只是我将使用 lsof 或 /proc//fd 的输出的 awk 解析的数字)可以直接传递给 close(int fd),我只是感到困惑。但是,我刚刚尝试过,它确实有效。
  • OP,无意冒犯,但是如果您使用 awk 解析 lsof 以关闭 C 程序中的套接字,那么您将直接偏离轨道。您需要考虑重新设计或给出一个非常有说服力的理由,说明这是必要的。

标签: c linux sockets unix tcp


【解决方案1】:

你有文件描述符。关掉不需要的就行!

在孩子中你应该关闭监听套接字。

在父级中,您应该关闭接受的套接字(=新连接)。

【讨论】:

    【解决方案2】:

    孩子继承父母的打开文件描述符(这也包括套接字)。在您的情况下,子进程还打开了套接字并绑定到上面的端口。因此,没有其他进程(在正常情况下)可以再监听这个端口了。您需要做的是关闭子中的套接字(或者,如果您在 fork() 之前不需要它)在分叉之前在父中关闭它。

    【讨论】:

      【解决方案3】:

      这是一种在您的孩子中确定您的文件描述符是否为套接字的方法。

      由于孩子将继承 fd 表,因此只需遍历 FD 测试每一个。以下程序中的子程序通过getrlimit 获取 fd 表的最大大小,并遍历该表以确定每个文件描述符是否 (a) 打开、(b) 套接字以及如果是 (c) 它是否是监听插座。父级只是在分叉之前打开一个侦听和常规套接字(用于测试目的),然后等待子级。

      您应该能够使用此大纲来完成您的目标,而无需求助于 awk 等。

      #define _BSD_SOURCE
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/wait.h>
      #include <unistd.h>
      #include <sys/socket.h>
      #include <sys/types.h>
      #include <errno.h>
      #include <sys/stat.h>
      #include <string.h>
      #include <netdb.h>
      
      
      int isListeningSocket(int fd)
      {
          int retval;
          socklen_t len = sizeof(retval);
      
          if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &retval, &len) != -1)
              if (retval)
                  return(1);
      
          return(0);
      }
      
      int main (int argc, char *argv[])
      {
          //create a listening socket
          int lsock = socket(AF_INET,SOCK_STREAM, 0);
      
          struct sockaddr_in serverinfo;
          memset(&serverinfo, '0', sizeof(serverinfo));
          serverinfo.sin_family=AF_INET;
          serverinfo.sin_port=htons(atoi("9999"));
          serverinfo.sin_addr.s_addr=INADDR_ANY;
      
          int ret;
      
          if ((ret = bind(lsock,(struct sockaddr *) &serverinfo, sizeof(serverinfo))) == -1)
          {
              perror("bind");
              exit(1);
          }
      
          if ((ret = listen(lsock,1000)) == -1)
          {
              perror("listen");
              exit(1);
          }
      
          //create a regular socket
          int rsock = socket(AF_INET,SOCK_STREAM, 0);
      
          int pid = fork();
      
          if (pid == -1)
          {
              perror("fork");
              exit(1);
          }
      
          if (pid) //parent
          {
              wait(NULL);
              exit(0);
          }
      
          //child ----------
      
          struct rlimit rlim;
      
          if ((ret = getrlimit(RLIMIT_NOFILE, &rlim)) == -1)
          {
              perror("getrlimit");
              exit(1);
          }
      
          int maxFD = rlim.rlim_cur;
      
          for (int i = 0; i < maxFD; ++i)
          {
             struct stat statbuf;
      
             if (fstat(i, &statbuf) == -1)
                 if (errno == EBADF)
                 {
                     printf("file descriptor %d is not open\n", i);
                     continue;
                 }
                 else
                 {
                     perror("fstat");
                     exit(1);
                 }
      
             if (S_ISSOCK(statbuf.st_mode))
                 if (isListeningSocket(i))
                    printf("file descriptor %d is a LISTENING socket\n", i);
                 else
                     printf("file descriptor %d is a REGULAR socket\n", i);
             else
                 printf("file descriptor %d is NOT a socket\n", i);
          }
      
          return 0;
      }
      

      【讨论】:

      • 完全没有必要。在 fork() 之后,子级具有相同的数据。与父级相同的变量。孩子所要做的就是关闭“lsock”或代码中调用的任何内容,而父母所要做的就是关闭“csock”或代码中调用的任何内容。你的 'rsock' 是做什么用的,我完全不知道。
      • @EJP - 我知道。我两天前说过,但这是 OP 坚持他想做的事情。
      • @EJP - 如果您在做出无根据且粗鲁的投票之前花 10 秒时间考虑它,您就不会逃脱。
      • @Duck 非常感谢。虽然,由于我不在城里,直到现在我都无法尝试,但是是的,getrlimit 和 fstat 对我有帮助,并且是正确的解决方案。
      猜你喜欢
      • 2016-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-18
      • 2011-08-18
      • 1970-01-01
      • 2023-03-11
      • 2011-10-08
      相关资源
      最近更新 更多