async socket python как асинхронно дождаться возможности писать/читать сокет
Играюсь с socket. С обычной версией я вроде разобрался и теперь хочу написать асинхронную. Например простой эхо сервер. Есть код:
async def _run(self) -> None:
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.bind((settings.HOST, settings.PORT))
self.server_socket.listen(8)
self.server_socket.setblocking(False)
loop = asyncio.get_event_loop()
while True:
client, address = await loop.sock_accept(self.server_socket)
print(f"Connected {address}")
loop.create_task(self.handle_client(client, address))
async def handle_client(self, client_socket: socket.socket, address: tuple) -> None:
while True:
data = client_socket.recv(1024).decode(encoding=settings.ENCODING) # BlockingIOError: [WinError 10035] Операция на незаблокированном сокете не может быть завершена немедленно
if not data:
break
client_socket.send(data.upper().encode(encoding=settings.ENCODING))
print(data)
Он ломается с ошибкой:
BlockingIOError: [WinError 10035] Операция на незаблокированном сокете не может быть завершена немедленно
Я примерно понимаю почему: стоит self.server_socket.setblocking(False) для реализации асинхронности и клиент ещё не прочитал прошлые данные или просто ещё не готов. Если перед client_socket.recv поставить time.sleep, то всё начинает работать, но это явно не решение.
Как мне асинхронно (чтобы другие клиенты в этот момент могли обрабатываться) дождаться момента, когда я смогу прочитать/написать что-либо?
P.S. Я знаю про asyncore, но хочу написать всё с нуля и понять как всё работает изнутри
Ответы (1 шт):
Можно сделать пример рабочим, если заменить
client_socket.recv(1024)
на
(await asyncio.get_event_loop().sock_recv(client_socket, 1024)).
(получение loop'а лучше вынести)
Что происходит внутри - можно посмотриеть в исходниках CPython
Если коротко, то операционная система предоставляет возможность для файлового дескриптора(в сокете он тоже присутствует) получать события, в том числе событие возможности чтения.
Делается это с помощью селектора(Poll, Epoll, ...). При этом селектору передаётся за какими файловыми дескрипторами и событиями надо следить.