【问题标题】:fork multiple processes to listen()/accept()fork 多个进程来监听()/接受()
【发布时间】:2021-02-12 08:12:33
【问题描述】:
struct sockaddr_in srv;
fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, ...);

srv.sin_family = AF_INET;
srv.sin_addr.s_addr = INADDR_ANY;
srv.sin_port = htons(9900);
bind(fd, (const struct sockaddr *)&srv, sizeof srv);

for (i = 0; i < MAX_PROCS; i++) {
   pid = fork();
   if (pid == 0) {
       /* child context */
       listen(fd, MAXCONN);
       connfd = accept(fd, NULL, NULL);
       ...
       close(connfd);
   } else 
}

为简洁起见,我删除了错误检查和其他部分。此代码有效,我可以在 localhost:9900 上 telnet 并查看连接。不过

% lsof -i :9900

报告所有 LISTEN 连接都属于具有相同 inode 的套接字,这认为这是有道理的,因为套接字在父节点中创建一次,并且每个子节点都共享它。

所以我想如果我想在同一个address:port 元组上真正有多个听众,我应该对每个孩子执行 bind() - listen() 序列。对吗?

但是这会导致错误:bind() 返回 EINVAL,我只创建了一个孩子:

for (i = 0; i < MAX_PROCS; i++) {
    pid = fork();
    if (pid == 0) {
       srv.sin_family = AF_INET;
       srv.sin_addr.s_addr = INADDR_ANY;
       srv.sin_port = htons(9900);
       bind(fd, (const struct sockaddr *)&srv, sizeof srv);
       ...
       listen();
       accept();
    }
}

我做错了什么?

【问题讨论】:

  • 检查返回值和errno。第二个(以及后续)可能因 EADDRINUSE 而失败
  • @wildplasser。谢谢你的答复。所以 SO_REUSEPORT 选项不够用?
  • @wildplasser。可能是我不清楚——bind() 为循环中的所有人返回 EINVAL,除了第一个。
  • 只检查返回值。这是有原因的。
  • @wildplasser,正如我之前写的,返回值为 EINVAL。

标签: c unix process fork


【解决方案1】:

可移植的Unix方式是在主进程/线程中调用socketbindlistenaccept 然后被子进程/线程调用。

您可能希望在bind 之前设置SO_REUSEADDR 选项,以便快速重启您的服务器。


在Linux上,每个孩子都可以创建自己的socket,设置SO_REUSEPORT,然后调用bindlistenaccept。见man socket(7)

SO_REUSEPORT

允许将多个AF_INETAF_INET6 套接字绑定到一个 相同的套接字地址。此选项必须在每个 调用bind之前的socket(包括第一个socket) 插座上。为防止端口劫持,绑定到同一地址的所有进程必须具有相同的有效 UID。此选项可用于 TCP 和 UDP 插座。

对于 TCP 套接字,此选项允许通过为每个线程使用不同的侦听器套接字来改进多线程服务器中的 accept 负载分配。这提供了改进的 与传统技术相比,负载分布 使用分配连接的单个accepting 线程,或具有与accept 竞争的多个线程 来自同一个套接字。

对于 UDP 套接字,使用该选项可以提供更好的 将传入的数据报分发到多个进程(或 线程)与具有的传统技术相比 多个进程竞争接收同一数据报 插座。

【讨论】:

  • 感谢您的反馈。在循环中为每个创建的孩子设置 SO_REUSEPORTSO_REUSEADDR 并没有帮助,即 bind() 仍然会因 EINVAL 而失败。创建每个子套接字,设置 SO_REUSEPORT 然后有效地绑定侦听接受与多次运行程序相同,并且确实有效。
  • @Mark 你的第一句话听起来不像你在做正确的事。在主进程中,您执行socketSO_REUSEADDRbindlisten。在孩子你只做accept
  • 在父级中只有一个监听器是一个瓶颈。我正在尝试做的(我仍在学习套接字编程)是创建多个侦听器(在同一个address:port 上),每个侦听器都在自己的子进程中。我知道这无法扩展,并且首选 pthread,但现在我只想让这个简单的事情正常工作。
猜你喜欢
  • 1970-01-01
  • 2021-12-05
  • 2013-06-17
  • 2013-10-23
  • 2015-11-30
  • 2018-09-25
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
相关资源
最近更新 更多