3

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

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

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

谢谢

4

3 回答 3

4

你有文件描述符。只需关闭您不需要的那些!

在孩子中,您应该关闭侦听套接字。

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

于 2013-10-11T13:51:29.220 回答
3

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

于 2013-10-11T13:55:42.787 回答
1

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

由于孩子将继承 fd 表,因此只需遍历 FD 测试每一个。以下程序中的子程序通过获取 fd 表的最大大小getrlimit并遍历该表,确定每个文件描述符是否 (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;
}
于 2013-10-13T17:03:36.983 回答