Skip to content

Latest commit

 

History

History
287 lines (181 loc) · 8.3 KB

udp_clients.rst

File metadata and controls

287 lines (181 loc) · 8.3 KB

How-to — UDP Client Endpoints


The UDP clients expect a :class:`.DatagramProtocol` instance to communicate with the remote endpoint.

.. seealso::

   :doc:`protocols`
      Explains what a :class:`.DatagramProtocol` is and how to use it.


Important

If you are familiar with UDP sockets, you know that there are no real connections (communication pipes) like there are with TCP sockets.

If not, I advise you to read the Unix manual pages :manpage:`udp(7)` and :manpage:`connect(2)`.

You need the host address (domain name or IP) and the port of connection in order to connect to the remote host:

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/basics/api_sync/connection_example1.py
         :linenos:

      .. note::

         The client does nothing when it enters the :keyword:`with` context. Everything is done on object creation.

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/basics/api_async/connection_example1.py
         :linenos:

      .. note::

         The call to :meth:`~.AsyncUDPNetworkClient.wait_connected` is required to actually initialize the client,
         since we cannot perform asynchronous operations at object creation.
         This is what the client does when it enters the the :keyword:`async with` context.

         Once completed, :meth:`~.AsyncUDPNetworkClient.wait_connected` is a no-op.


If you have your own way to obtain a connected :class:`socket.socket` instance, you can pass it to the client.

If the socket is not connected, an :exc:`OSError` is raised.

Important

It must be a :data:`~socket.SOCK_DGRAM` socket with :data:`~socket.AF_INET` or :data:`~socket.AF_INET6` family.

Warning

The resource ownership is given to the client. You must close the client to close the socket.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/basics/api_sync/socket_example1.py
         :linenos:

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/basics/api_async/socket_example1.py
         :linenos:

      .. note::

         Even with a ready-to-use socket, the call to :meth:`~.AsyncUDPNetworkClient.wait_connected` is still required.


There's not much to say, except that objects passed as arguments are automatically converted to bytes to send to the remote host thanks to the :term:`protocol object`.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: send_packet_example1
         :start-after: [start]
         :dedent:
         :linenos:

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: send_packet_example1
         :start-after: [start]
         :dedent:
         :linenos:


You get the next available packet, already parsed.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: recv_packet_example1
         :start-after: [start]
         :dedent:
         :linenos:

      You can control the receive timeout with the ``timeout`` parameter:

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: recv_packet_example2
         :start-after: [start]
         :dedent:
         :linenos:

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: recv_packet_example1
         :start-after: [start]
         :dedent:
         :linenos:

      You can control the receive timeout by adding a timeout scope using the :term:`asynchronous framework` :

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: recv_packet_example2
         :start-after: [start]
         :dedent:
         :linenos:


Tip

Remember to catch invalid data parsing errors.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: recv_packet_example3
         :start-after: [start]
         :dedent:
         :linenos:
         :emphasize-lines: 3-4

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: recv_packet_example3
         :start-after: [start]
         :dedent:
         :linenos:
         :emphasize-lines: 4-5

You can use iter_received_packets() to get all the received packets in a sequence or a set.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: recv_packet_example4
         :start-after: [start]
         :dedent:
         :linenos:

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: recv_packet_example4
         :start-after: [start]
         :dedent:
         :linenos:

The timeout parameter defaults to zero to get only the data already in the buffer, but you can change it.

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: recv_packet_example5
         :start-after: [start]
         :dedent:
         :linenos:

      .. seealso::

         :meth:`.UDPNetworkClient.iter_received_packets`
            The method description and usage (especially for the ``timeout`` parameter).

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: recv_packet_example5
         :start-after: [start]
         :dedent:
         :linenos:

      .. seealso::

         :meth:`.AsyncUDPNetworkClient.iter_received_packets`
            The method description and usage (especially for the ``timeout`` parameter).


For low-level operations such as :meth:`~socket.socket.setsockopt`, the client object exposes the socket through a :class:`.SocketProxy`:

.. tabs::

   .. group-tab:: Synchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_sync.py
         :pyobject: socket_proxy_example
         :start-after: [start]
         :dedent:
         :linenos:

   .. group-tab:: Asynchronous

      .. literalinclude:: ../_include/examples/howto/udp_clients/usage/api_async.py
         :pyobject: socket_proxy_example
         :start-after: [start]
         :dedent:
         :linenos:

      .. warning::

         Make sure that :meth:`~.AsyncUDPNetworkClient.wait_connected` has been called before.


.. tabs::

   .. group-tab:: Synchronous

      All client methods are thread-safe (such as :class:`asyncio.Lock`).
      Synchronization follows these rules:

      * :meth:`~.UDPNetworkClient.send_packet` and :meth:`~.UDPNetworkClient.recv_packet` do not share the same
        :class:`threading.Lock` instance.

      * :meth:`~.UDPNetworkClient.close` will not wait for :meth:`~.UDPNetworkClient.recv_packet`.

      * The :attr:`client.socket <.UDPNetworkClient.socket>` methods are also thread-safe. This means that you cannot access
        the underlying socket methods (e.g. :meth:`~socket.socket.getsockopt`) during a write operation.

   .. group-tab:: Asynchronous

      All client methods do not require external task synchronization. Synchronization follows these rules:

      * :meth:`~.AsyncUDPNetworkClient.send_packet` and :meth:`~.AsyncUDPNetworkClient.recv_packet` do not share the same lock instance.

      * :meth:`~.AsyncUDPNetworkClient.aclose` will not wait for :meth:`~.AsyncUDPNetworkClient.recv_packet`.