1039 lines
32 KiB
ReStructuredText
1039 lines
32 KiB
ReStructuredText
.. currentmodule:: asyncio
|
|
|
|
|
|
.. _asyncio-transports-protocols:
|
|
|
|
|
|
========================
|
|
Transports and Protocols
|
|
========================
|
|
|
|
.. rubric:: Preface
|
|
|
|
Transports and Protocols are used by the **low-level** event loop
|
|
APIs such as :meth:`loop.create_connection`. They use
|
|
callback-based programming style and enable high-performance
|
|
implementations of network or IPC protocols (e.g. HTTP).
|
|
|
|
Essentially, transports and protocols should only be used in
|
|
libraries and frameworks and never in high-level asyncio
|
|
applications.
|
|
|
|
This documentation page covers both `Transports`_ and `Protocols`_.
|
|
|
|
.. rubric:: Introduction
|
|
|
|
At the highest level, the transport is concerned with *how* bytes
|
|
are transmitted, while the protocol determines *which* bytes to
|
|
transmit (and to some extent when).
|
|
|
|
A different way of saying the same thing: a transport is an
|
|
abstraction for a socket (or similar I/O endpoint) while a protocol
|
|
is an abstraction for an application, from the transport's point
|
|
of view.
|
|
|
|
Yet another view is the transport and protocol interfaces
|
|
together define an abstract interface for using network I/O and
|
|
interprocess I/O.
|
|
|
|
There is always a 1:1 relationship between transport and protocol
|
|
objects: the protocol calls transport methods to send data,
|
|
while the transport calls protocol methods to pass it data that
|
|
has been received.
|
|
|
|
Most of connection oriented event loop methods
|
|
(such as :meth:`loop.create_connection`) usually accept a
|
|
*protocol_factory* argument used to create a *Protocol* object
|
|
for an accepted connection, represented by a *Transport* object.
|
|
Such methods usually return a tuple of ``(transport, protocol)``.
|
|
|
|
.. rubric:: Contents
|
|
|
|
This documentation page contains the following sections:
|
|
|
|
* The `Transports`_ section documents asyncio :class:`BaseTransport`,
|
|
:class:`ReadTransport`, :class:`WriteTransport`, :class:`Transport`,
|
|
:class:`DatagramTransport`, and :class:`SubprocessTransport`
|
|
classes.
|
|
|
|
* The `Protocols`_ section documents asyncio :class:`BaseProtocol`,
|
|
:class:`Protocol`, :class:`BufferedProtocol`,
|
|
:class:`DatagramProtocol`, and :class:`SubprocessProtocol` classes.
|
|
|
|
* The `Examples`_ section showcases how to work with transports,
|
|
protocols, and low-level event loop APIs.
|
|
|
|
|
|
.. _asyncio-transport:
|
|
|
|
Transports
|
|
==========
|
|
|
|
Transports are classes provided by :mod:`asyncio` in order to abstract
|
|
various kinds of communication channels.
|
|
|
|
Transport objects are always instantiated by an
|
|
:ref:`asyncio event loop <asyncio-event-loop>`.
|
|
|
|
asyncio implements transports for TCP, UDP, SSL, and subprocess pipes.
|
|
The methods available on a transport depend on the transport's kind.
|
|
|
|
The transport classes are :ref:`not thread safe <asyncio-multithreading>`.
|
|
|
|
|
|
Transports Hierarchy
|
|
--------------------
|
|
|
|
.. class:: BaseTransport
|
|
|
|
Base class for all transports. Contains methods that all
|
|
asyncio transports share.
|
|
|
|
.. class:: WriteTransport(BaseTransport)
|
|
|
|
A base transport for write-only connections.
|
|
|
|
Instances of the *WriteTransport* class are returned from
|
|
the :meth:`loop.connect_write_pipe` event loop method and
|
|
are also used by subprocess-related methods like
|
|
:meth:`loop.subprocess_exec`.
|
|
|
|
.. class:: ReadTransport(BaseTransport)
|
|
|
|
A base transport for read-only connections.
|
|
|
|
Instances of the *ReadTransport* class are returned from
|
|
the :meth:`loop.connect_read_pipe` event loop method and
|
|
are also used by subprocess-related methods like
|
|
:meth:`loop.subprocess_exec`.
|
|
|
|
.. class:: Transport(WriteTransport, ReadTransport)
|
|
|
|
Interface representing a bidirectional transport, such as a
|
|
TCP connection.
|
|
|
|
The user does not instantiate a transport directly; they call a
|
|
utility function, passing it a protocol factory and other
|
|
information necessary to create the transport and protocol.
|
|
|
|
Instances of the *Transport* class are returned from or used by
|
|
event loop methods like :meth:`loop.create_connection`,
|
|
:meth:`loop.create_unix_connection`,
|
|
:meth:`loop.create_server`, :meth:`loop.sendfile`, etc.
|
|
|
|
|
|
.. class:: DatagramTransport(BaseTransport)
|
|
|
|
A transport for datagram (UDP) connections.
|
|
|
|
Instances of the *DatagramTransport* class are returned from
|
|
the :meth:`loop.create_datagram_endpoint` event loop method.
|
|
|
|
|
|
.. class:: SubprocessTransport(BaseTransport)
|
|
|
|
An abstraction to represent a connection between a parent and its
|
|
child OS process.
|
|
|
|
Instances of the *SubprocessTransport* class are returned from
|
|
event loop methods :meth:`loop.subprocess_shell` and
|
|
:meth:`loop.subprocess_exec`.
|
|
|
|
|
|
Base Transport
|
|
--------------
|
|
|
|
.. method:: BaseTransport.close()
|
|
|
|
Close the transport.
|
|
|
|
If the transport has a buffer for outgoing
|
|
data, buffered data will be flushed asynchronously. No more data
|
|
will be received. After all buffered data is flushed, the
|
|
protocol's :meth:`protocol.connection_lost()
|
|
<BaseProtocol.connection_lost>` method will be called with
|
|
:const:`None` as its argument.
|
|
|
|
.. method:: BaseTransport.is_closing()
|
|
|
|
Return ``True`` if the transport is closing or is closed.
|
|
|
|
.. method:: BaseTransport.get_extra_info(name, default=None)
|
|
|
|
Return information about the transport or underlying resources
|
|
it uses.
|
|
|
|
*name* is a string representing the piece of transport-specific
|
|
information to get.
|
|
|
|
*default* is the value to return if the information is not
|
|
available, or if the transport does not support querying it
|
|
with the given third-party event loop implementation or on the
|
|
current platform.
|
|
|
|
For example, the following code attempts to get the underlying
|
|
socket object of the transport::
|
|
|
|
sock = transport.get_extra_info('socket')
|
|
if sock is not None:
|
|
print(sock.getsockopt(...))
|
|
|
|
Categories of information that can be queried on some transports:
|
|
|
|
* socket:
|
|
|
|
- ``'peername'``: the remote address to which the socket is
|
|
connected, result of :meth:`socket.socket.getpeername`
|
|
(``None`` on error)
|
|
|
|
- ``'socket'``: :class:`socket.socket` instance
|
|
|
|
- ``'sockname'``: the socket's own address,
|
|
result of :meth:`socket.socket.getsockname`
|
|
|
|
* SSL socket:
|
|
|
|
- ``'compression'``: the compression algorithm being used as a
|
|
string, or ``None`` if the connection isn't compressed; result
|
|
of :meth:`ssl.SSLSocket.compression`
|
|
|
|
- ``'cipher'``: a three-value tuple containing the name of the
|
|
cipher being used, the version of the SSL protocol that defines
|
|
its use, and the number of secret bits being used; result of
|
|
:meth:`ssl.SSLSocket.cipher`
|
|
|
|
- ``'peercert'``: peer certificate; result of
|
|
:meth:`ssl.SSLSocket.getpeercert`
|
|
|
|
- ``'sslcontext'``: :class:`ssl.SSLContext` instance
|
|
|
|
- ``'ssl_object'``: :class:`ssl.SSLObject` or
|
|
:class:`ssl.SSLSocket` instance
|
|
|
|
* pipe:
|
|
|
|
- ``'pipe'``: pipe object
|
|
|
|
* subprocess:
|
|
|
|
- ``'subprocess'``: :class:`subprocess.Popen` instance
|
|
|
|
.. method:: BaseTransport.set_protocol(protocol)
|
|
|
|
Set a new protocol.
|
|
|
|
Switching protocol should only be done when both
|
|
protocols are documented to support the switch.
|
|
|
|
.. method:: BaseTransport.get_protocol()
|
|
|
|
Return the current protocol.
|
|
|
|
|
|
Read-only Transports
|
|
--------------------
|
|
|
|
.. method:: ReadTransport.is_reading()
|
|
|
|
Return ``True`` if the transport is receiving new data.
|
|
|
|
.. versionadded:: 3.7
|
|
|
|
.. method:: ReadTransport.pause_reading()
|
|
|
|
Pause the receiving end of the transport. No data will be passed to
|
|
the protocol's :meth:`protocol.data_received() <Protocol.data_received>`
|
|
method until :meth:`resume_reading` is called.
|
|
|
|
.. versionchanged:: 3.7
|
|
The method is idempotent, i.e. it can be called when the
|
|
transport is already paused or closed.
|
|
|
|
.. method:: ReadTransport.resume_reading()
|
|
|
|
Resume the receiving end. The protocol's
|
|
:meth:`protocol.data_received() <Protocol.data_received>` method
|
|
will be called once again if some data is available for reading.
|
|
|
|
.. versionchanged:: 3.7
|
|
The method is idempotent, i.e. it can be called when the
|
|
transport is already reading.
|
|
|
|
|
|
Write-only Transports
|
|
---------------------
|
|
|
|
.. method:: WriteTransport.abort()
|
|
|
|
Close the transport immediately, without waiting for pending operations
|
|
to complete. Buffered data will be lost. No more data will be received.
|
|
The protocol's :meth:`protocol.connection_lost()
|
|
<BaseProtocol.connection_lost>` method will eventually be
|
|
called with :const:`None` as its argument.
|
|
|
|
.. method:: WriteTransport.can_write_eof()
|
|
|
|
Return :const:`True` if the transport supports
|
|
:meth:`~WriteTransport.write_eof`, :const:`False` if not.
|
|
|
|
.. method:: WriteTransport.get_write_buffer_size()
|
|
|
|
Return the current size of the output buffer used by the transport.
|
|
|
|
.. method:: WriteTransport.get_write_buffer_limits()
|
|
|
|
Get the *high* and *low* watermarks for write flow control. Return a
|
|
tuple ``(low, high)`` where *low* and *high* are positive number of
|
|
bytes.
|
|
|
|
Use :meth:`set_write_buffer_limits` to set the limits.
|
|
|
|
.. versionadded:: 3.4.2
|
|
|
|
.. method:: WriteTransport.set_write_buffer_limits(high=None, low=None)
|
|
|
|
Set the *high* and *low* watermarks for write flow control.
|
|
|
|
These two values (measured in number of
|
|
bytes) control when the protocol's
|
|
:meth:`protocol.pause_writing() <BaseProtocol.pause_writing>`
|
|
and :meth:`protocol.resume_writing() <BaseProtocol.resume_writing>`
|
|
methods are called. If specified, the low watermark must be less
|
|
than or equal to the high watermark. Neither *high* nor *low*
|
|
can be negative.
|
|
|
|
:meth:`~BaseProtocol.pause_writing` is called when the buffer size
|
|
becomes greater than or equal to the *high* value. If writing has
|
|
been paused, :meth:`~BaseProtocol.resume_writing` is called when
|
|
the buffer size becomes less than or equal to the *low* value.
|
|
|
|
The defaults are implementation-specific. If only the
|
|
high watermark is given, the low watermark defaults to an
|
|
implementation-specific value less than or equal to the
|
|
high watermark. Setting *high* to zero forces *low* to zero as
|
|
well, and causes :meth:`~BaseProtocol.pause_writing` to be called
|
|
whenever the buffer becomes non-empty. Setting *low* to zero causes
|
|
:meth:`~BaseProtocol.resume_writing` to be called only once the
|
|
buffer is empty. Use of zero for either limit is generally
|
|
sub-optimal as it reduces opportunities for doing I/O and
|
|
computation concurrently.
|
|
|
|
Use :meth:`~WriteTransport.get_write_buffer_limits`
|
|
to get the limits.
|
|
|
|
.. method:: WriteTransport.write(data)
|
|
|
|
Write some *data* bytes to the transport.
|
|
|
|
This method does not block; it buffers the data and arranges for it
|
|
to be sent out asynchronously.
|
|
|
|
.. method:: WriteTransport.writelines(list_of_data)
|
|
|
|
Write a list (or any iterable) of data bytes to the transport.
|
|
This is functionally equivalent to calling :meth:`write` on each
|
|
element yielded by the iterable, but may be implemented more
|
|
efficiently.
|
|
|
|
.. method:: WriteTransport.write_eof()
|
|
|
|
Close the write end of the transport after flushing all buffered data.
|
|
Data may still be received.
|
|
|
|
This method can raise :exc:`NotImplementedError` if the transport
|
|
(e.g. SSL) doesn't support half-closed connections.
|
|
|
|
|
|
Datagram Transports
|
|
-------------------
|
|
|
|
.. method:: DatagramTransport.sendto(data, addr=None)
|
|
|
|
Send the *data* bytes to the remote peer given by *addr* (a
|
|
transport-dependent target address). If *addr* is :const:`None`,
|
|
the data is sent to the target address given on transport
|
|
creation.
|
|
|
|
This method does not block; it buffers the data and arranges
|
|
for it to be sent out asynchronously.
|
|
|
|
.. method:: DatagramTransport.abort()
|
|
|
|
Close the transport immediately, without waiting for pending
|
|
operations to complete. Buffered data will be lost.
|
|
No more data will be received. The protocol's
|
|
:meth:`protocol.connection_lost() <BaseProtocol.connection_lost>`
|
|
method will eventually be called with :const:`None` as its argument.
|
|
|
|
|
|
.. _asyncio-subprocess-transports:
|
|
|
|
Subprocess Transports
|
|
---------------------
|
|
|
|
.. method:: SubprocessTransport.get_pid()
|
|
|
|
Return the subprocess process id as an integer.
|
|
|
|
.. method:: SubprocessTransport.get_pipe_transport(fd)
|
|
|
|
Return the transport for the communication pipe corresponding to the
|
|
integer file descriptor *fd*:
|
|
|
|
* ``0``: readable streaming transport of the standard input (*stdin*),
|
|
or :const:`None` if the subprocess was not created with ``stdin=PIPE``
|
|
* ``1``: writable streaming transport of the standard output (*stdout*),
|
|
or :const:`None` if the subprocess was not created with ``stdout=PIPE``
|
|
* ``2``: writable streaming transport of the standard error (*stderr*),
|
|
or :const:`None` if the subprocess was not created with ``stderr=PIPE``
|
|
* other *fd*: :const:`None`
|
|
|
|
.. method:: SubprocessTransport.get_returncode()
|
|
|
|
Return the subprocess return code as an integer or :const:`None`
|
|
if it hasn't returned, which is similar to the
|
|
:attr:`subprocess.Popen.returncode` attribute.
|
|
|
|
.. method:: SubprocessTransport.kill()
|
|
|
|
Kill the subprocess.
|
|
|
|
On POSIX systems, the function sends SIGKILL to the subprocess.
|
|
On Windows, this method is an alias for :meth:`terminate`.
|
|
|
|
See also :meth:`subprocess.Popen.kill`.
|
|
|
|
.. method:: SubprocessTransport.send_signal(signal)
|
|
|
|
Send the *signal* number to the subprocess, as in
|
|
:meth:`subprocess.Popen.send_signal`.
|
|
|
|
.. method:: SubprocessTransport.terminate()
|
|
|
|
Stop the subprocess.
|
|
|
|
On POSIX systems, this method sends SIGTERM to the subprocess.
|
|
On Windows, the Windows API function TerminateProcess() is called to
|
|
stop the subprocess.
|
|
|
|
See also :meth:`subprocess.Popen.terminate`.
|
|
|
|
.. method:: SubprocessTransport.close()
|
|
|
|
Kill the subprocess by calling the :meth:`kill` method.
|
|
|
|
If the subprocess hasn't returned yet, and close transports of
|
|
*stdin*, *stdout*, and *stderr* pipes.
|
|
|
|
|
|
.. _asyncio-protocol:
|
|
|
|
Protocols
|
|
=========
|
|
|
|
asyncio provides a set of abstract base classes that should be used
|
|
to implement network protocols. Those classes are meant to be used
|
|
together with :ref:`transports <asyncio-transport>`.
|
|
|
|
Subclasses of abstract base protocol classes may implement some or
|
|
all methods. All these methods are callbacks: they are called by
|
|
transports on certain events, for example when some data is received.
|
|
A base protocol method should be called by the corresponding transport.
|
|
|
|
|
|
Base Protocols
|
|
--------------
|
|
|
|
.. class:: BaseProtocol
|
|
|
|
Base protocol with methods that all protocols share.
|
|
|
|
.. class:: Protocol(BaseProtocol)
|
|
|
|
The base class for implementing streaming protocols
|
|
(TCP, Unix sockets, etc).
|
|
|
|
.. class:: BufferedProtocol(BaseProtocol)
|
|
|
|
A base class for implementing streaming protocols with manual
|
|
control of the receive buffer.
|
|
|
|
.. class:: DatagramProtocol(BaseProtocol)
|
|
|
|
The base class for implementing datagram (UDP) protocols.
|
|
|
|
.. class:: SubprocessProtocol(BaseProtocol)
|
|
|
|
The base class for implementing protocols communicating with child
|
|
processes (unidirectional pipes).
|
|
|
|
|
|
Base Protocol
|
|
-------------
|
|
|
|
All asyncio protocols can implement Base Protocol callbacks.
|
|
|
|
.. rubric:: Connection Callbacks
|
|
|
|
Connection callbacks are called on all protocols, exactly once per
|
|
a successful connection. All other protocol callbacks can only be
|
|
called between those two methods.
|
|
|
|
.. method:: BaseProtocol.connection_made(transport)
|
|
|
|
Called when a connection is made.
|
|
|
|
The *transport* argument is the transport representing the
|
|
connection. The protocol is responsible for storing the reference
|
|
to its transport.
|
|
|
|
.. method:: BaseProtocol.connection_lost(exc)
|
|
|
|
Called when the connection is lost or closed.
|
|
|
|
The argument is either an exception object or :const:`None`.
|
|
The latter means a regular EOF is received, or the connection was
|
|
aborted or closed by this side of the connection.
|
|
|
|
|
|
.. rubric:: Flow Control Callbacks
|
|
|
|
Flow control callbacks can be called by transports to pause or
|
|
resume writing performed by the protocol.
|
|
|
|
See the documentation of the :meth:`~WriteTransport.set_write_buffer_limits`
|
|
method for more details.
|
|
|
|
.. method:: BaseProtocol.pause_writing()
|
|
|
|
Called when the transport's buffer goes over the high watermark.
|
|
|
|
.. method:: BaseProtocol.resume_writing()
|
|
|
|
Called when the transport's buffer drains below the low watermark.
|
|
|
|
If the buffer size equals the high watermark,
|
|
:meth:`~BaseProtocol.pause_writing` is not called: the buffer size must
|
|
go strictly over.
|
|
|
|
Conversely, :meth:`~BaseProtocol.resume_writing` is called when the
|
|
buffer size is equal or lower than the low watermark. These end
|
|
conditions are important to ensure that things go as expected when
|
|
either mark is zero.
|
|
|
|
|
|
Streaming Protocols
|
|
-------------------
|
|
|
|
Event methods, such as :meth:`loop.create_server`,
|
|
:meth:`loop.create_unix_server`, :meth:`loop.create_connection`,
|
|
:meth:`loop.create_unix_connection`, :meth:`loop.connect_accepted_socket`,
|
|
:meth:`loop.connect_read_pipe`, and :meth:`loop.connect_write_pipe`
|
|
accept factories that return streaming protocols.
|
|
|
|
.. method:: Protocol.data_received(data)
|
|
|
|
Called when some data is received. *data* is a non-empty bytes
|
|
object containing the incoming data.
|
|
|
|
Whether the data is buffered, chunked or reassembled depends on
|
|
the transport. In general, you shouldn't rely on specific semantics
|
|
and instead make your parsing generic and flexible. However,
|
|
data is always received in the correct order.
|
|
|
|
The method can be called an arbitrary number of times while
|
|
a connection is open.
|
|
|
|
However, :meth:`protocol.eof_received() <Protocol.eof_received>`
|
|
is called at most once. Once `eof_received()` is called,
|
|
``data_received()`` is not called anymore.
|
|
|
|
.. method:: Protocol.eof_received()
|
|
|
|
Called when the other end signals it won't send any more data
|
|
(for example by calling :meth:`transport.write_eof()
|
|
<WriteTransport.write_eof>`, if the other end also uses
|
|
asyncio).
|
|
|
|
This method may return a false value (including ``None``), in which case
|
|
the transport will close itself. Conversely, if this method returns a
|
|
true value, the protocol used determines whether to close the transport.
|
|
Since the default implementation returns ``None``, it implicitly closes the
|
|
connection.
|
|
|
|
Some transports, including SSL, don't support half-closed connections,
|
|
in which case returning true from this method will result in the connection
|
|
being closed.
|
|
|
|
|
|
State machine:
|
|
|
|
.. code-block:: none
|
|
|
|
start -> connection_made
|
|
[-> data_received]*
|
|
[-> eof_received]?
|
|
-> connection_lost -> end
|
|
|
|
|
|
Buffered Streaming Protocols
|
|
----------------------------
|
|
|
|
.. versionadded:: 3.7
|
|
**Important:** this has been added to asyncio in Python 3.7
|
|
*on a provisional basis*! This is as an experimental API that
|
|
might be changed or removed completely in Python 3.8.
|
|
|
|
Buffered Protocols can be used with any event loop method
|
|
that supports `Streaming Protocols`_.
|
|
|
|
``BufferedProtocol`` implementations allow explicit manual allocation
|
|
and control of the receive buffer. Event loops can then use the buffer
|
|
provided by the protocol to avoid unnecessary data copies. This
|
|
can result in noticeable performance improvement for protocols that
|
|
receive big amounts of data. Sophisticated protocol implementations
|
|
can significantly reduce the number of buffer allocations.
|
|
|
|
The following callbacks are called on :class:`BufferedProtocol`
|
|
instances:
|
|
|
|
.. method:: BufferedProtocol.get_buffer(sizehint)
|
|
|
|
Called to allocate a new receive buffer.
|
|
|
|
*sizehint* is the recommended minimum size for the returned
|
|
buffer. It is acceptable to return smaller or larger buffers
|
|
than what *sizehint* suggests. When set to -1, the buffer size
|
|
can be arbitrary. It is an error to return a buffer with a zero size.
|
|
|
|
``get_buffer()`` must return an object implementing the
|
|
:ref:`buffer protocol <bufferobjects>`.
|
|
|
|
.. method:: BufferedProtocol.buffer_updated(nbytes)
|
|
|
|
Called when the buffer was updated with the received data.
|
|
|
|
*nbytes* is the total number of bytes that were written to the buffer.
|
|
|
|
.. method:: BufferedProtocol.eof_received()
|
|
|
|
See the documentation of the :meth:`protocol.eof_received()
|
|
<Protocol.eof_received>` method.
|
|
|
|
|
|
:meth:`~BufferedProtocol.get_buffer` can be called an arbitrary number
|
|
of times during a connection. However, :meth:`protocol.eof_received()
|
|
<Protocol.eof_received>` is called at most once
|
|
and, if called, :meth:`~BufferedProtocol.get_buffer` and
|
|
:meth:`~BufferedProtocol.buffer_updated` won't be called after it.
|
|
|
|
State machine:
|
|
|
|
.. code-block:: none
|
|
|
|
start -> connection_made
|
|
[-> get_buffer
|
|
[-> buffer_updated]?
|
|
]*
|
|
[-> eof_received]?
|
|
-> connection_lost -> end
|
|
|
|
|
|
Datagram Protocols
|
|
------------------
|
|
|
|
Datagram Protocol instances should be constructed by protocol
|
|
factories passed to the :meth:`loop.create_datagram_endpoint` method.
|
|
|
|
.. method:: DatagramProtocol.datagram_received(data, addr)
|
|
|
|
Called when a datagram is received. *data* is a bytes object containing
|
|
the incoming data. *addr* is the address of the peer sending the data;
|
|
the exact format depends on the transport.
|
|
|
|
.. method:: DatagramProtocol.error_received(exc)
|
|
|
|
Called when a previous send or receive operation raises an
|
|
:class:`OSError`. *exc* is the :class:`OSError` instance.
|
|
|
|
This method is called in rare conditions, when the transport (e.g. UDP)
|
|
detects that a datagram could not be delivered to its recipient.
|
|
In many conditions though, undeliverable datagrams will be silently
|
|
dropped.
|
|
|
|
.. note::
|
|
|
|
On BSD systems (macOS, FreeBSD, etc.) flow control is not supported
|
|
for datagram protocols, because there is no reliable way to detect send
|
|
failures caused by writing too many packets.
|
|
|
|
The socket always appears 'ready' and excess packets are dropped. An
|
|
:class:`OSError` with ``errno`` set to :const:`errno.ENOBUFS` may
|
|
or may not be raised; if it is raised, it will be reported to
|
|
:meth:`DatagramProtocol.error_received` but otherwise ignored.
|
|
|
|
|
|
.. _asyncio-subprocess-protocols:
|
|
|
|
Subprocess Protocols
|
|
--------------------
|
|
|
|
Datagram Protocol instances should be constructed by protocol
|
|
factories passed to the :meth:`loop.subprocess_exec` and
|
|
:meth:`loop.subprocess_shell` methods.
|
|
|
|
.. method:: SubprocessProtocol.pipe_data_received(fd, data)
|
|
|
|
Called when the child process writes data into its stdout or stderr
|
|
pipe.
|
|
|
|
*fd* is the integer file descriptor of the pipe.
|
|
|
|
*data* is a non-empty bytes object containing the received data.
|
|
|
|
.. method:: SubprocessProtocol.pipe_connection_lost(fd, exc)
|
|
|
|
Called when one of the pipes communicating with the child process
|
|
is closed.
|
|
|
|
*fd* is the integer file descriptor that was closed.
|
|
|
|
.. method:: SubprocessProtocol.process_exited()
|
|
|
|
Called when the child process has exited.
|
|
|
|
|
|
Examples
|
|
========
|
|
|
|
.. _asyncio_example_tcp_echo_server_protocol:
|
|
|
|
TCP Echo Server
|
|
---------------
|
|
|
|
Create a TCP echo server using the :meth:`loop.create_server` method, send back
|
|
received data, and close the connection::
|
|
|
|
import asyncio
|
|
|
|
|
|
class EchoServerProtocol(asyncio.Protocol):
|
|
def connection_made(self, transport):
|
|
peername = transport.get_extra_info('peername')
|
|
print('Connection from {}'.format(peername))
|
|
self.transport = transport
|
|
|
|
def data_received(self, data):
|
|
message = data.decode()
|
|
print('Data received: {!r}'.format(message))
|
|
|
|
print('Send: {!r}'.format(message))
|
|
self.transport.write(data)
|
|
|
|
print('Close the client socket')
|
|
self.transport.close()
|
|
|
|
|
|
async def main():
|
|
# Get a reference to the event loop as we plan to use
|
|
# low-level APIs.
|
|
loop = asyncio.get_running_loop()
|
|
|
|
server = await loop.create_server(
|
|
lambda: EchoServerProtocol(),
|
|
'127.0.0.1', 8888)
|
|
|
|
async with server:
|
|
await server.serve_forever()
|
|
|
|
|
|
asyncio.run(main())
|
|
|
|
|
|
.. seealso::
|
|
|
|
The :ref:`TCP echo server using streams <asyncio-tcp-echo-server-streams>`
|
|
example uses the high-level :func:`asyncio.start_server` function.
|
|
|
|
.. _asyncio_example_tcp_echo_client_protocol:
|
|
|
|
TCP Echo Client
|
|
---------------
|
|
|
|
A TCP echo client using the :meth:`loop.create_connection` method, sends
|
|
data, and waits until the connection is closed::
|
|
|
|
import asyncio
|
|
|
|
|
|
class EchoClientProtocol(asyncio.Protocol):
|
|
def __init__(self, message, on_con_lost):
|
|
self.message = message
|
|
self.on_con_lost = on_con_lost
|
|
|
|
def connection_made(self, transport):
|
|
transport.write(self.message.encode())
|
|
print('Data sent: {!r}'.format(self.message))
|
|
|
|
def data_received(self, data):
|
|
print('Data received: {!r}'.format(data.decode()))
|
|
|
|
def connection_lost(self, exc):
|
|
print('The server closed the connection')
|
|
self.on_con_lost.set_result(True)
|
|
|
|
|
|
async def main():
|
|
# Get a reference to the event loop as we plan to use
|
|
# low-level APIs.
|
|
loop = asyncio.get_running_loop()
|
|
|
|
on_con_lost = loop.create_future()
|
|
message = 'Hello World!'
|
|
|
|
transport, protocol = await loop.create_connection(
|
|
lambda: EchoClientProtocol(message, on_con_lost),
|
|
'127.0.0.1', 8888)
|
|
|
|
# Wait until the protocol signals that the connection
|
|
# is lost and close the transport.
|
|
try:
|
|
await on_con_lost
|
|
finally:
|
|
transport.close()
|
|
|
|
|
|
asyncio.run(main())
|
|
|
|
|
|
.. seealso::
|
|
|
|
The :ref:`TCP echo client using streams <asyncio-tcp-echo-client-streams>`
|
|
example uses the high-level :func:`asyncio.connect` function.
|
|
|
|
|
|
.. _asyncio-udp-echo-server-protocol:
|
|
|
|
UDP Echo Server
|
|
---------------
|
|
|
|
A UDP echo server, using the :meth:`loop.create_datagram_endpoint`
|
|
method, sends back received data::
|
|
|
|
import asyncio
|
|
|
|
|
|
class EchoServerProtocol:
|
|
def connection_made(self, transport):
|
|
self.transport = transport
|
|
|
|
def datagram_received(self, data, addr):
|
|
message = data.decode()
|
|
print('Received %r from %s' % (message, addr))
|
|
print('Send %r to %s' % (message, addr))
|
|
self.transport.sendto(data, addr)
|
|
|
|
|
|
async def main():
|
|
print("Starting UDP server")
|
|
|
|
# Get a reference to the event loop as we plan to use
|
|
# low-level APIs.
|
|
loop = asyncio.get_running_loop()
|
|
|
|
# One protocol instance will be created to serve all
|
|
# client requests.
|
|
transport, protocol = await loop.create_datagram_endpoint(
|
|
lambda: EchoServerProtocol(),
|
|
local_addr=('127.0.0.1', 9999))
|
|
|
|
try:
|
|
await asyncio.sleep(3600) # Serve for 1 hour.
|
|
finally:
|
|
transport.close()
|
|
|
|
|
|
asyncio.run(main())
|
|
|
|
|
|
.. _asyncio-udp-echo-client-protocol:
|
|
|
|
UDP Echo Client
|
|
---------------
|
|
|
|
A UDP echo client, using the :meth:`loop.create_datagram_endpoint`
|
|
method, sends data and closes the transport when it receives the answer::
|
|
|
|
import asyncio
|
|
|
|
|
|
class EchoClientProtocol:
|
|
def __init__(self, message, on_con_lost):
|
|
self.message = message
|
|
self.on_con_lost = on_con_lost
|
|
self.transport = None
|
|
|
|
def connection_made(self, transport):
|
|
self.transport = transport
|
|
print('Send:', self.message)
|
|
self.transport.sendto(self.message.encode())
|
|
|
|
def datagram_received(self, data, addr):
|
|
print("Received:", data.decode())
|
|
|
|
print("Close the socket")
|
|
self.transport.close()
|
|
|
|
def error_received(self, exc):
|
|
print('Error received:', exc)
|
|
|
|
def connection_lost(self, exc):
|
|
print("Connection closed")
|
|
self.on_con_lost.set_result(True)
|
|
|
|
|
|
async def main():
|
|
# Get a reference to the event loop as we plan to use
|
|
# low-level APIs.
|
|
loop = asyncio.get_running_loop()
|
|
|
|
on_con_lost = loop.create_future()
|
|
message = "Hello World!"
|
|
|
|
transport, protocol = await loop.create_datagram_endpoint(
|
|
lambda: EchoClientProtocol(message, on_con_lost),
|
|
remote_addr=('127.0.0.1', 9999))
|
|
|
|
try:
|
|
await on_con_lost
|
|
finally:
|
|
transport.close()
|
|
|
|
|
|
asyncio.run(main())
|
|
|
|
|
|
.. _asyncio_example_create_connection:
|
|
|
|
Connecting Existing Sockets
|
|
---------------------------
|
|
|
|
Wait until a socket receives data using the
|
|
:meth:`loop.create_connection` method with a protocol::
|
|
|
|
import asyncio
|
|
import socket
|
|
|
|
|
|
class MyProtocol(asyncio.Protocol):
|
|
|
|
def __init__(self, on_con_lost):
|
|
self.transport = None
|
|
self.on_con_lost = on_con_lost
|
|
|
|
def connection_made(self, transport):
|
|
self.transport = transport
|
|
|
|
def data_received(self, data):
|
|
print("Received:", data.decode())
|
|
|
|
# We are done: close the transport;
|
|
# connection_lost() will be called automatically.
|
|
self.transport.close()
|
|
|
|
def connection_lost(self, exc):
|
|
# The socket has been closed
|
|
self.on_con_lost.set_result(True)
|
|
|
|
|
|
async def main():
|
|
# Get a reference to the event loop as we plan to use
|
|
# low-level APIs.
|
|
loop = asyncio.get_running_loop()
|
|
on_con_lost = loop.create_future()
|
|
|
|
# Create a pair of connected sockets
|
|
rsock, wsock = socket.socketpair()
|
|
|
|
# Register the socket to wait for data.
|
|
transport, protocol = await loop.create_connection(
|
|
lambda: MyProtocol(on_con_lost), sock=rsock)
|
|
|
|
# Simulate the reception of data from the network.
|
|
loop.call_soon(wsock.send, 'abc'.encode())
|
|
|
|
try:
|
|
await protocol.on_con_lost
|
|
finally:
|
|
transport.close()
|
|
wsock.close()
|
|
|
|
asyncio.run(main())
|
|
|
|
.. seealso::
|
|
|
|
The :ref:`watch a file descriptor for read events
|
|
<asyncio_example_watch_fd>` example uses the low-level
|
|
:meth:`loop.add_reader` method to register an FD.
|
|
|
|
The :ref:`register an open socket to wait for data using streams
|
|
<asyncio_example_create_connection-streams>` example uses high-level streams
|
|
created by the :func:`asyncio.connect` function in a coroutine.
|
|
|
|
.. _asyncio_example_subprocess_proto:
|
|
|
|
loop.subprocess_exec() and SubprocessProtocol
|
|
---------------------------------------------
|
|
|
|
An example of a subprocess protocol used to get the output of a
|
|
subprocess and to wait for the subprocess exit.
|
|
|
|
The subprocess is created by th :meth:`loop.subprocess_exec` method::
|
|
|
|
import asyncio
|
|
import sys
|
|
|
|
class DateProtocol(asyncio.SubprocessProtocol):
|
|
def __init__(self, exit_future):
|
|
self.exit_future = exit_future
|
|
self.output = bytearray()
|
|
|
|
def pipe_data_received(self, fd, data):
|
|
self.output.extend(data)
|
|
|
|
def process_exited(self):
|
|
self.exit_future.set_result(True)
|
|
|
|
async def get_date():
|
|
# Get a reference to the event loop as we plan to use
|
|
# low-level APIs.
|
|
loop = asyncio.get_running_loop()
|
|
|
|
code = 'import datetime; print(datetime.datetime.now())'
|
|
exit_future = asyncio.Future(loop=loop)
|
|
|
|
# Create the subprocess controlled by DateProtocol;
|
|
# redirect the standard output into a pipe.
|
|
transport, protocol = await loop.subprocess_exec(
|
|
lambda: DateProtocol(exit_future),
|
|
sys.executable, '-c', code,
|
|
stdin=None, stderr=None)
|
|
|
|
# Wait for the subprocess exit using the process_exited()
|
|
# method of the protocol.
|
|
await exit_future
|
|
|
|
# Close the stdout pipe.
|
|
transport.close()
|
|
|
|
# Read the output which was collected by the
|
|
# pipe_data_received() method of the protocol.
|
|
data = bytes(protocol.output)
|
|
return data.decode('ascii').rstrip()
|
|
|
|
date = asyncio.run(get_date())
|
|
print(f"Current date: {date}")
|
|
|
|
See also the :ref:`same example <asyncio_example_create_subprocess_exec>`
|
|
written using high-level APIs.
|