| .. 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 | |
| ========== | |
| **Source code:** :source:`Lib/asyncio/transports.py` | |
| ---------------------------------------------------- | |
| 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. The transport should not be | |
| used once it is closed. | |
| .. 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. | |
| .. versionchanged:: 3.13 | |
| This method can be called with an empty bytes object to send a | |
| zero-length datagram. The buffer size calculation used for flow | |
| control is also updated to account for the datagram header. | |
| .. 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``: writable streaming transport of the standard input (*stdin*), | |
| or :const:`None` if the subprocess was not created with ``stdin=PIPE`` | |
| * ``1``: readable streaming transport of the standard output (*stdout*), | |
| or :const:`None` if the subprocess was not created with ``stdout=PIPE`` | |
| * ``2``: readable 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 :py:const:`~signal.SIGTERM` to the subprocess. | |
| On Windows, the Windows API function :c:func:`!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 | |
| ========= | |
| **Source code:** :source:`Lib/asyncio/protocols.py` | |
| --------------------------------------------------- | |
| 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 | |
| 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 | |
| -------------------- | |
| Subprocess 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. | |
| It can be called before :meth:`~SubprocessProtocol.pipe_data_received` and | |
| :meth:`~SubprocessProtocol.pipe_connection_lost` methods. | |
| 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( | |
| 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.open_connection` 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( | |
| 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:`open_connection` 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 the :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() | |
| self.pipe_closed = False | |
| self.exited = False | |
| def pipe_connection_lost(self, fd, exc): | |
| self.pipe_closed = True | |
| self.check_for_exit() | |
| def pipe_data_received(self, fd, data): | |
| self.output.extend(data) | |
| def process_exited(self): | |
| self.exited = True | |
| # process_exited() method can be called before | |
| # pipe_connection_lost() method: wait until both methods are | |
| # called. | |
| self.check_for_exit() | |
| def check_for_exit(self): | |
| if self.pipe_closed and self.exited: | |
| 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 as dt; print(dt.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. |