****************************************** Alternative — Unix Stream Client Endpoints ****************************************** .. include:: ../../_include/sync-async-variants.rst .. contents:: Table of Contents :local: ------ The Protocol Object =================== The UNIX stream clients expect a :class:`.StreamProtocol` instance to communicate with the peer endpoint. .. seealso:: :doc:`/howto/protocols` Explains what a :class:`.StreamProtocol` is and how to use it. Connecting To Peer ================== You need the listening socket's filepath: .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_sync/connection_example1.py :linenos: You can control the connection timeout with the ``connect_timeout`` parameter: .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_sync/connection_example2.py :pyobject: main :lineno-match: :emphasize-lines: 5-11 .. note:: The client does nothing when it enters the :keyword:`with` context. Everything is done on object creation. .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_async/connection_example1.py :linenos: You can control the connection timeout by adding a timeout scope using the :term:`asynchronous framework`: .. tabs:: .. group-tab:: Using ``asyncio`` .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_async/connection_example2_asyncio.py :pyobject: main :lineno-match: :emphasize-lines: 5-13 .. group-tab:: Using ``trio`` .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_async/connection_example2_trio.py :pyobject: main :lineno-match: :emphasize-lines: 5-13 .. group-tab:: Using the ``AsyncBackend`` API .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_async/connection_example2_backend_api.py :pyobject: main :lineno-match: :emphasize-lines: 5-13 .. note:: The call to :meth:`~.AsyncUnixStreamClient.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:`~.AsyncUnixStreamClient.wait_connected` is a no-op. Using An Already Connected Socket ================================= 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_STREAM` socket with :data:`~socket.AF_UNIX` 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/alternatives/unix_stream_clients/basics/api_sync/socket_example1.py :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/basics/api_async/socket_example1.py :linenos: .. note:: Even with a ready-to-use socket, the call to :meth:`~.AsyncUnixStreamClient.wait_connected` is still required. Sending Packets =============== 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/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: send_packet_example1 :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: send_packet_example1 :start-after: [start] :dedent: :linenos: Sending Packets with socket control messages -------------------------------------------- By using :class:`.SocketAncillary`, you can send SCM data. See the Unix manual page :manpage:`sendmsg(2)` for details. .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: send_packet_with_ancillary_example1 :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: send_packet_with_ancillary_example1 :start-after: [start] :dedent: :linenos: Receiving Packets ================= You get the next available packet, already parsed. Extraneous data is kept for the next call. .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_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/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: recv_packet_example2 :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_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`: .. tabs:: .. group-tab:: Using ``asyncio`` .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example2_asyncio :start-after: [start] :dedent: :linenos: .. group-tab:: Using ``trio`` .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example2_trio :start-after: [start] :dedent: :linenos: .. group-tab:: Using the ``AsyncBackend`` API .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example2_backend_api :start-after: [start] :dedent: :linenos: .. tip:: Remember to catch invalid data parsing errors. .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: recv_packet_example3 :start-after: [start] :dedent: :linenos: :emphasize-lines: 3-4 .. group-tab:: Asynchronous .. tabs:: .. group-tab:: Using ``asyncio`` .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example3_asyncio :start-after: [start] :dedent: :linenos: :emphasize-lines: 4-5 .. group-tab:: Using ``trio`` .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example3_trio :start-after: [start] :dedent: :linenos: :emphasize-lines: 4-5 .. group-tab:: Using the ``AsyncBackend`` API .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example3_backend_api :start-after: [start] :dedent: :linenos: :emphasize-lines: 4-5 Receiving Packets with socket control messages ---------------------------------------------- By using :class:`.SocketAncillary`, you can receive SCM data. See the Unix manual page :manpage:`recvmsg(2)` for details. .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: recv_packet_with_ancillary_example1 :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_with_ancillary_example1 :start-after: [start] :dedent: :linenos: .. tip:: The default buffer size for this operation is approximately 8 KiB. However, you can customize this behavior. .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: recv_packet_with_ancillary_example2 :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_with_ancillary_example2 :start-after: [start] :dedent: :linenos: Receiving Multiple Packets At Once ================================== 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/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: recv_packet_example4 :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_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/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: recv_packet_example5 :start-after: [start] :dedent: :linenos: .. seealso:: :meth:`.UnixStreamClient.iter_received_packets` The method description and usage (especially for the ``timeout`` parameter). .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: recv_packet_example5 :start-after: [start] :dedent: :linenos: .. seealso:: :meth:`.AsyncUnixStreamClient.iter_received_packets` The method description and usage (especially for the ``timeout`` parameter). Close The Write-End Stream ========================== If you are sure you will never reuse ``send_packet()``, you can call ``send_eof()`` to shut down the write stream. .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: send_eof_example :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: send_eof_example :start-after: [start] :dedent: :linenos: .. note:: ``send_eof()`` will block until all unsent data has been flushed before closing the stream. Peer Credentials ================ Get the peer's process information with ``get_peer_credentials()``: .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: socket_peer_credentials_example :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: socket_peer_credentials_example :start-after: [start] :dedent: :linenos: .. warning:: Make sure that :meth:`~.AsyncUnixStreamClient.wait_connected` has been called before. Low-Level Socket Operations =========================== 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/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: socket_proxy_example :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: socket_proxy_example :start-after: [start] :dedent: :linenos: .. warning:: Make sure that :meth:`~.AsyncUnixStreamClient.wait_connected` has been called before. ``socket.recv()`` Buffer Size ============================= By default, the client uses a reasonable buffer size when calling ``recv_packet()``. You can control this value by setting the ``max_recv_size`` parameter: .. tabs:: .. group-tab:: Synchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_sync.py :pyobject: max_recv_size_example :start-after: [start] :dedent: :linenos: .. group-tab:: Asynchronous .. literalinclude:: ../../_include/examples/alternatives/unix_stream_clients/usage/api_async.py :pyobject: max_recv_size_example :start-after: [start] :dedent: :linenos: .. note:: ``max_recv_size`` is also used as a size hint for :term:`buffered serializers `. See :ref:`this section ` for details. Concurrency And Multithreading ============================== .. tabs:: .. group-tab:: Synchronous All client methods are thread-safe. Synchronization follows these rules: * :meth:`~.UnixStreamClient.send_packet` and :meth:`~.UnixStreamClient.recv_packet` do not share the same :class:`threading.Lock` instance. * :meth:`~.UnixStreamClient.close` will not wait for :meth:`~.UnixStreamClient.recv_packet`. * The :attr:`client.socket <.UnixStreamClient.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 (such as :class:`asyncio.Lock`). Synchronization follows these rules: * :meth:`~.AsyncUnixStreamClient.send_packet` and :meth:`~.AsyncUnixStreamClient.recv_packet` do not share the same lock instance. * :meth:`~.AsyncUnixStreamClient.aclose` will not wait for :meth:`~.AsyncUnixStreamClient.recv_packet`.