Понимание протокола Python Asyncio - PullRequest
0 голосов
/ 28 июня 2018

Мне не хватает понимания того, как работают Python asyncio и протоколы.

Документация, которая представляется актуальной:
class asyncio.Protocol
Protocol.eof_received() конечный автомат показывает соответствующие переходы AbstractEventLoop.run_until_complete(future)

Пример кода, который является клиентом echo tcp:

import asyncio

class EchoClientProtocol(asyncio.Protocol):
    def __init__(self, message, loop):
        self.message = message.encode()

    def connection_made(self, transport):
        self.transport = transport
        self.write_data()

    def data_received(self, data):
        print('Data received: {!r}',len(data))
        self.write_data()

    def eof_received(self):
        print("eof")
        return True

    def write_data(self):
        print("write")
        self.transport.write(self.message)

    def connection_lost(self, exc):
        print('The server closed the connection')
        print('Stop the event loop')


loop = asyncio.get_event_loop()
message = 'Hello World!'

coro = loop.create_connection(lambda: EchoClientProtocol(message, loop),
                              '127.0.0.1', 5676)
loop.run_until_complete(coro)
print("done")

Вывод на эхо-сервере при обработке:

write
Data received: {!r} 12
write
done

Насколько я понимаю, это должно работать, пока соединение не будет закрыто.

От: Обратные вызовы соединения

connection_made () и connection_lost () вызываются ровно один раз для каждого успешного соединения. Все остальные обратные вызовы будут вызываться между этими двумя методами, что упрощает управление ресурсами в реализации протокола.

и из конечного автомата:

start 
-> connection_made
[-> data_received]*
[-> eof_received]?
-> connection_lost 
-> end

Но функция EchoClientProtocol.connection_lost никогда не вызывается и loop.run_until_complete(coro) завершается до завершения протокола.

Вопрос:
Как получить сопрограмму / будущее, которая оборачивает протокол таким образом, что сопрограмма завершается, когда достигается конечное состояние протокола и loop.run_until_complete возвращает такое событие.

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Как указывает ответ Сроу, loop.create_connection - сопрограмма, которая возвращается, как только она создала пару транспорт / протокол. Поэтому вам нужно запустить другую сопрограмму (или эквивалентный объект), которая будет поддерживать цикл событий для того, чтобы происходили интересные вещи.

как запустить цикл, пока внутреннее состояние протокола не достигнет конца состояния без явного закрытия цикла

Вам не обязательно закрывать или останавливать цикл. Если я правильно следую за вами, вы бы хотели избежать неприглядного run_forever, вместо этого написав что-то вроде:

transport, protocol = loop.run_until_complete(coro)
transport.close()
loop.run_until_complete(protocol.wait_connection_lost())

Хотя wait_connection_lost() действительно не поставляется с asyncio, вы предоставляете реализацию протокола, так что вы легко можете создать ее для своего протокола:

class EchoClientProtocol(asyncio.Protocol):
    # your other methods are unchanged

    def __init__(self, message, loop):
        self.message = message.encode()
        self.__done = loop.create_future()

    def connection_lost(self, exc):
        # the value passed to set_result will be transmitted to
        # run_until_complete(protocol.wait_connection_lost()).
        self.__done.set_result(None)

    # When awaited, resumes execution after connection_lost()
    # has been invoked on this protocol.
    def wait_connection_lost(self):
        return self.__done
0 голосов
/ 28 июня 2018

loop.run_until_complete(coro) возвращает transport, protocol.

Таким образом, чтобы переключить connection_lost, соединение должно быть закрыто либо сервером, либо клиентом. Итак, вам нужно:

transport, _ = loop.run_until_complete(coro)
transport.close()
loop.run_forever()
print("done")

Поскольку вы не останавливаете цикл в connection_lost, он будет заблокирован здесь навсегда.

Кстати, coro возвращается при успешном подключении.

...