Проблемы с epoll и socket accept у разветвленных детей

У меня есть небольшой скрипт сервера/клиента, написанный на Python.

server.py создает новый сокет и порождает двух дочерних элементов. в дочернем процессе я регистрирую сокет des criptor для события EPOLLIN, поэтому я могу принять сокет в дочернем процессе. Проблема в том, что оба дочерних элемента уведомляются о запросе клиента, НО только один дочерний элемент принимает, а другой выдает исключение «[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, НО только один действительно принимает, другой выдает исключение.


person user2624744    schedule 05.11.2014    source источник
comment
Можно ли просто игнорировать ошибку во втором клиенте?   -  person Tom Dalton    schedule 05.11.2014


Ответы (2)


Это работает так, как задумано: вы хотите, чтобы все дети получали уведомление, если они могут принять нового клиента, и все они получают уведомление. На данный момент epoll возвращается, оба могут вызывать accept. Но если подключается только один клиент, то accept может вернуть успех только один раз. Если подключаются несколько клиентов, то другой прием также будет успешным.

Таким образом, вы действительно хотите, чтобы проснулось столько дочерних элементов, сколько клиентов в настоящее время находятся в очереди на прослушивание. К сожалению, нет возможности сделать что-то подобное с API, определенным системой (это не проблема python). Таким образом, обычный способ — просто игнорировать EAGAIN и ждать следующего клиента.

person Steffen Ullrich    schedule 05.11.2014

«Ресурс временно недоступен» — это EAGAIN, и в данном случае это означает, что вы не можете accept() в это время, что естественно, поскольку другой процесс уже сделал accept(). EAGAIN не является фатальной ошибкой, и в этом случае ее можно просто игнорировать. Или вы можете обрабатывать accept() в основном процессе и использовать очередь для обработки подключенных сокетов к рабочим процессам.

Для реального использования (в отличие от экспериментов и обучения) я настоятельно рекомендую использовать рабочую среду, такую ​​как Twisted.

person Anton    schedule 05.11.2014