Skip to content

Latest commit

 

History

History
176 lines (106 loc) · 4.98 KB

echo_client_server_tcp.rst

File metadata and controls

176 lines (106 loc) · 4.98 KB

An Echo Client/Server Over TCP

To see how to create a server and a client with the minimum requirements, let's create a server that will return everything sent by a connected client.


Before doing all this networking stuff, you need to know what you want to transmit and in what format. It is your :term:`communication protocol`.

There is a bunch of serializers available in :mod:`easynetwork.serializers` for everyone to enjoy:

For the tutorial, :class:`.JSONSerializer` will be used.

For communication via TCP, a :class:`.StreamProtocol` :term:`protocol object` must be created.

.. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/json_protocol.py
   :linenos:
   :caption: json_protocol.py

Note

Of course, you are under no obligation to write a subclass. But see :ref:`this note <why-write-a-protocol-subclass>` for details.

Now that we have established the :term:`communication protocol`, we can create our server.

First, you must create a request handler class by subclassing the :class:`.AsyncStreamRequestHandler` class and overriding its :meth:`~.AsyncStreamRequestHandler.handle` method; this method will process incoming requests.

.. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/echo_request_handler.py
   :linenos:
   :caption: echo_request_handler.py

Note

Pay attention to :meth:`~.AsyncStreamRequestHandler.handle`, it is an :std:term:`asynchronous generator` function. All requests sent by the client are literally injected into the generator via the :keyword:`yield` statement.

.. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/echo_request_handler.py
   :start-at: async def handle
   :end-at: return
   :lineno-match:
   :emphasize-lines: 6
   :dedent:

You can :keyword:`yield` several times if you want to wait for a new packet from the client in the same context.

Warning

Leaving the generator will not close the connection, a new generator will be created afterwards. You may, however, explicitly close the connection if you want to:

await client.aclose()

Second, you must instantiate the TCP server class, passing it the server's address, the :term:`protocol object` instance, and the request handler instance.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/server.py
         :linenos:
         :caption: server.py

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/async_server.py
         :linenos:
         :caption: server.py

Note

Setting host to :data:`None` will bind the server to all interfaces. This means the server is ready to accept connections with IPv4 and IPv6 addresses (if available).

This is the client side:

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/client.py
         :linenos:
         :caption: client.py

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/tutorials/echo_client_server_tcp/async_client.py
         :linenos:
         :caption: client.py


The output of the example should look something like this:

Server:

.. tabs::

   .. group-tab:: IPv4 connection

      .. code-block:: console

         (.venv) $ python server.py
         127.0.0.1 sent {'command-line arguments': ['Hello', 'world!']}
         127.0.0.1 sent {'command-line arguments': ['Python', 'is', 'nice']}

   .. group-tab:: IPv6 connection

      .. code-block:: console

         (.venv) $ python server.py
         ::1 sent {'command-line arguments': ['Hello', 'world!']}
         ::1 sent {'command-line arguments': ['Python', 'is', 'nice']}

Client:

(.venv) $ python client.py Hello world!
Sent:     {'command-line arguments': ['Hello', 'world!']}
Received: {'command-line arguments': ['Hello', 'world!']}
(.venv) $ python client.py Python is nice
Sent:     {'command-line arguments': ['Python', 'is', 'nice']}
Received: {'command-line arguments': ['Python', 'is', 'nice']}