【问题标题】:Linux C socket how to prevent 2 forked process from accepting the same connection when using epoll?Linux C socket在使用epoll时如何防止2个fork进程接受同一个连接?
【发布时间】:2021-12-12 00:42:16
【问题描述】:
for (int t = 0; t < physicalCoreCount; t++) {
        int pid = fork();
        if (pid==0) {
            // setting up epoll    
            epoll_fd = epoll_create1(0);                
            event.data.fd = listenSocketfd;
            event.events = EPOLLIN | EPOLLET;
            ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenSocketfd, &event);                
            events = (epoll_event*)calloc(LISTENQ, sizeof(event));
           
            //*****/
            while (1) {
                int ndfs = epoll_wait(epoll_fd, events, curfdCount, -1);
                if (ndfs==-1) {                        
                    continue;
                }
                for (int i=0; i < ndfs; i++) {
                    if (events[i].data.fd == listenSocketfd) { // original listener
                        int new_connfd = accept(events[i].data.fd, (sockaddr*)&clientaddr, &clientlen);
                        if (new_connfd==-1) {
                            if (errno==EAGAIN || errno==EWOULDBLOCK) {
                                continue;
                            }
                            else exitPerrorLog("accept()");
                        }

                        set_non_block(new_connfd);
                        event.events = EPOLLIN | EPOLLET;
                        event.data.fd = new_connfd;
                        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_connfd, &event) < 0)
                            exitPerrorLog("epoll_ctl");

                        clientaddrOfFd[new_connfd] = new sockaddr_in();
                        memcpy(clientaddrOfFd[new_connfd], &clientaddr, sizeof(clientaddr));
                        curfdCount++;
                    }   
                    else {
                        process(events[i].data.fd, clientaddrOfFd[events[i].data.fd]);
                        //epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &event);                           
                        if (curfdCount > 10000) curfdCount = 10000; //curfdCount--;
                    }
                }
            }
        }
    }

出现问题是因为我正在尝试实现持久连接(响应后未关闭)。然而,孩子 0 可以接受()一个套接字文件描述符 X,处理它,但后来孩子 1 可以接受()相同的文件描述符。由于我没有关闭子 0 上的连接(以实现 HTTP1.1 keep-alive),现在 2 个子正在读取/写入同一个文件描述符。

有什么方法可以防止这个问题?谢谢。

编辑: 重要更新:所以主要问题是我认为 2 个相同的 FD 意味着它是相同的连接,我想避免这种情况,因为它会导致 2 个孩子读/写相同的东西.如果这种情况没有发生(读/写重叠),那么我认为问题已经解决。有人可以确认吗?

【问题讨论】:

  • 只有一个进程可以接受特定的连接。
  • 两个子进程可能会从accept得到相同的fd返回值,但它们应该是不同的连接。使用getsockname检查每个孩子连接的客户端地址和端口号。
  • @IanAbbott 哦,我认为同一个 FD 意味着它是同一个客户端
  • @IanAbbott 但如果它确实来自同一个客户端,那么让 2 个子进程接受 2 个连接的性能是否比只接受一次的性能低?我希望重用(已建立的)连接
  • “我认为同一个 FD 意味着它是同一个客户端”——每个子节点在分叉之后都有相同的一组正在使用的文件描述符(因为父节点之间没有关闭或打开更多的文件描述符) forks),因此每个子节点的最低未使用文件描述符将是相同的。 accept() 调用将返回最低的未使用文件描述符,这对于每个孩子都是相同的。

标签: c sockets fork epoll persistent-connection


【解决方案1】:

您正在创建多个 epoll 实例并在每个实例的侦听套接字上注册边缘触发事件。自然地,当一个新的连接可以接受时,你会从每个人那里得到一个事件。但是,只有一个进程可以成功接受每个连接。正如在 cmets 中观察到的,两个不同的子进程可能会接受在各自进程中分配了相同文件描述符编号的连接,但这并不意味着它们引用的是同一个套接字。

您有多种选择,但其中最突出的是:

  • 使用单个 epoll 实例,由所有进程共享。您可以通过让父级在分叉任何子级之前创建它来自动获得它。在这种情况下,只有一个孩子会收到每个边缘触发的事件。当然,如果孩子们打算注册只应该由他们接收的事件,那么这将不会很好地工作。

  • 只需接受(没有双关语)当连接可用时多个进程将接收事件并处理它。这似乎就是你现在正在做的事情(通过忽略来自accept()EAGAINEWOULDBLOCK 错误),我看不出你不应该继续这样做的特别原因。

【讨论】:

  • 让 2 个孩子接受来自同一个客户端的 2 个连接比重用已经建立的连接慢吗?基本上这个问题只是因为我想检测一个连接是否已经存在,如果是,那么使用它。但它不适用于这个模型。我已经尝试了您列出的选项,但它不会解决这个特定问题(即重用而不是创建新连接)。
  • 或者我应该让 2 个孩子为同一个客户端提供 2 个不同的连接?
  • @HuyLe,原则上,重用现有连接可能比需要建立新连接更快。但是,如果为每个请求建立新连接仍然足够快,那么额外的复杂性可能不值得。此外,尽管我接受您的应用程序未按您希望的那样工作,但问题声称多个进程接受相同的连接是不合理的。问题的真正性质尚不清楚,甚至很难判断它是否与问题中提供的代码有关。
  • 我在我的编辑中澄清了这个问题。所以“接受的连接”不一样,它们只是具有相同的文件描述符编号。例如孩子 1 和 2 都得到 (new_connfd = accept()) == 5。在这种情况下,我是否遇到任何读/写重叠问题(因为 2 个进程正在写入同一个 FD 号)?
  • @HuyLe,对accept()的不同调用提供的套接字是不同的,独立的,无论是什么进程,文件描述符号,还是远程对等点。在您的示例场景中,不,通过具有相同文件描述符编号的套接字进行通信的两个独立进程之间没有串扰。
猜你喜欢
  • 2019-04-30
  • 1970-01-01
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
  • 2015-02-06
  • 1970-01-01
相关资源
最近更新 更多