Post

Dive into Python’s asyncio, part 5 - protocols

Protocols are asyncio's primitives supplied as convenient base classes to quickly set up clients or servers using TCP/UDP (+ subprocesses). These are especially helpful when we need to implement low level handling of protocol of some sort. I believe they are inspired by Twisted's protcols.

Simple example of TCP echo server protocol may be (taken from asyncio docs):

import asyncio

class EchoServerClientProtocol(asyncio.Protocol):  # 1
    def connection_made(self, transport):  # 2
        self.transport = transport

    def data_received(self, data):  # 3
        self.transport.write(data)

    def connection_lost(self, exc):  # 4
        print('Connection ended')


loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)  # 5
server = loop.run_until_complete(coro)

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
  1. First thing is to inherit from asyncio-provided base class asyncio.Protocol. It serves for handling TCP and SSL based communication.
  2. Implementing given protocol comes down to writing particular callbacks, starting from connection_made which is invoked everytime we have new connection from the outside.
  3. data_received is called when client sends some data to the server
  4. connection_lost is invoked when connection ends either from client side or server.

It all seems nice and clean, but where are coroutines? Where is async and await? Well, they are not there. If one needs to introduce them, then they should be decoupled using asyncio.ensure_future:

class EchoServerClientProtocol(asyncio.Protocol):
    # ...

    def data_received(self, data):
        asyncio.ensure_future(self.some_coro, data)

    async def some_coro(self, data):
        await async_operation

 

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.