Python блокирует асинхронные операции с сокетами - PullRequest
0 голосов
/ 21 октября 2019

Я попытался создать программу, которая соединяет два сокета, записывая данные, считанные с одного сокета, в другой и наоборот.

import socket as sock
import asyncio

BUFF_SIZE = 4096


async def create_relay(s, port, loop):
    while True:
        s1, _ = await loop.sock_accept(s)  # while awaiting sock_accept no other task is run
        s2 = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
        await loop.sock_connect(s2, ('localhost', port))
        loop.create_task(relay(s1, s2, loop))
        loop.create_task(relay(s2, s1, loop))
        # await asyncio.sleep(1000)        # while awaiting sleep the relay tasks are run

async def relay(s1, s2, loop):
    data = await loop.sock_recv(s1, BUFF_SIZE)
    while data:
        await loop.sock_sendall(s2, data)
        data = await loop.sock_recv(s1, BUFF_SIZE)


def main():
    s = sock.socket(sock.AF_INET, sock.SOCK_STREAM)
    s.bind(('localhost', 9999))
    s.listen()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(create_relay(s, 9990, loop))


if __name__ == '__main__':
    main()

Пока эта программа успешно устанавливает сокеты, я могу 'на самом деле передавать любые сообщения. Отладка кода показала, что программа застревает на s1, _ = await loop.sock_accept(s) и не выполняет никакого другого асинхронного кода при await при другом соединении. Однако, если я добавлю еще await, например await asyncio.sleep(1000) в конце цикла create_relay, реле будет работать хотя бы частично, и я смогу пересылать сообщения в одном направлении.

Может кто-нибудь объяснить, почему этислучаются затворы и как их избежать?

1 Ответ

1 голос
/ 21 октября 2019

Может кто-нибудь объяснить, почему эти блокировки происходят и как их избежать?

Код не делает сокеты неблокирующими. Вам необходимо добавить:

s.setblocking(False)

в каждое место, где вы создаете или получаете новый сокет.

Также, sock_connect, sock_accept, sock_recv и sock_sendall считаются API-интерфейсами низкого уровня, которые не предназначены для непосредственного использования кодом приложения. Вместо этого ваш код должен использовать потоковые API 1017 *, которые предоставляют такие функции, как буферизация, и, конечно, автоматически создают неблокирующие сокеты.

...