【问题标题】:Problems with epoll and socket accept in forked children分叉子代中 epoll 和 socket 接受的问题
【发布时间】:2014-11-05 10:50:10
【问题描述】:

我有一个用 Python 编写的小服务器/客户端脚本。

server.py 创建一个新的套接字并产生两个孩子。 在子进程中,我为 EPOLLIN 事件注册了套接字 des criptor,所以 我可以在孩子的套接字上做一个接受。问题是,两个孩子都是 收到客户请求的通知,但只有一个孩子接受,另一个孩子 正在抛出异常“[Errno 11] 资源暂时不可用”。 如果所有孩子无法在套接字上接受,我该如何阻止他们收到通知?

---server.py---

import socket, time, os, select

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 10000))
sock.listen(256)
sock.setblocking(False)


for _ in range(0,2):

    pid = os.fork()
    if pid == 0:    #in child
        poll = select.epoll()
        poll.register(sock.fileno(), select.EPOLLIN)
        while True:
            events = poll.poll(2)   # listening for events with 2 sec timeout
            for fileno, event in events:
                if event & select.EPOLLIN:  # there is data on socket available
                    print("EPOLLIN in PID: " + str(os.getpid()))
                    try:
                        clientsock, addr = sock.accept()
                        clientsock.close()
                        print("accepted and closed in PID: " + str(os.getpid()))
                    except Exception as e:
                        print("PID: " +  str(os.getpid()) + " " + str(e))


# we are in parent process, keep it live
while True:
    time.sleep(10)

客户端只向服务器发出一个请求:

---client.py---

import socket, time, select, sys

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost',10000))
s.setblocking(False)
s.close()

执行“client.py”后,我从服务器得到以下输出:

EPOLLIN in PID: 9424
EPOLLIN in PID: 9425
PID: 9424 [Errno 11] Resource temporarily unavailable
accepted and closed in PID: 9425

如您所见,他们都获得了 EPOLLIN,但只有一个实际接受,另一个 一个抛出异常。

【问题讨论】:

  • 是否可以忽略第二个客户端的错误?

标签: python sockets fork epoll


【解决方案1】:

它按设计工作:您希望所有孩子都收到通知,如果他们可以接受新客户,他们都会收到通知。在epoll返回的那一刻,两者都可以调用accept。但是如果只有一个客户端连接,那么accept只能返回一次成功。如果多个客户端正在连接,那么另一个接受也会成功。

因此,您实际上想要的是只唤醒与当前在侦听队列中的客户端一样多的孩子。不幸的是,没有办法用系统定义的 API 来做这样的事情(这不是 python 的问题)。因此,通常的方法是忽略 EAGAIN 并等待下一个客户端。

【讨论】:

    【解决方案2】:

    “资源暂时不可用”是EAGAIN,在这种情况下,这意味着你此时不能accept(),这是很自然的,因为其他进程已经做了accept()。 EAGAIN 不是致命错误,在这种情况下可以忽略。或者,您可以在主进程中处理accept(),并使用队列来处理连接到工作进程的套接字。

    对于实际使用(与实验和学习相反),我强烈建议使用生产框架,例如 Twisted。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-25
      • 1970-01-01
      • 2021-12-12
      相关资源
      最近更新 更多