Документация Python неофициальный перевод

asyncio-protocol.md

771 строк · 53.8 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.14/library/asyncio-protocol.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Транспорты и протоколы89Предисловие1011Транспорты и протоколы используются **низкоуровневыми** API цикла событий, такими как [`loop.create_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_connection). Они используют стиль программирования на основе колбэков и позволяют создавать высокопроизводительные реализации сетевых протоколов и протоколов межпроцессного взаимодействия (например, HTTP).1213По существу, транспорты и протоколы следует использовать только в библиотеках и фреймворках, но никогда в высокоуровневых приложениях asyncio.1415Эта страница документации охватывает как [транспорты](https://python-all.ru/3.14/library/asyncio-protocol.html#transports), так и [протоколы](https://python-all.ru/3.14/library/asyncio-protocol.html#protocols).1617Введение1819На самом высоком уровне транспорт отвечает за *то, как* передаются байты, тогда как протокол определяет, *какие* байты передавать (и в некоторой степени – когда).2021Другими словами: транспорт – это абстракция для сокета (или аналогичной точки ввода-вывода), а протокол – это абстракция для приложения с точки зрения транспорта.2223Ещё один взгляд: интерфейсы транспорта и протокола вместе определяют абстрактный интерфейс для использования сетевого ввода-вывода и межпроцессного ввода-вывода.2425Между объектами транспорта и протокола всегда существует отношение 1:1: протокол вызывает методы транспорта для отправки данных, а транспорт вызывает методы протокола, чтобы передать ему полученные данные.2627Большинство методов цикла событий, ориентированных на соединения (таких как [`loop.create_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_connection)), обычно принимают аргумент *protocol\_factory*, используемый для создания объекта *Protocol* для принятого соединения, представленного объектом *Transport*. Такие методы обычно возвращают кортеж `(transport, protocol)`.2829Содержание3031Эта страница документации содержит следующие разделы:3233- Раздел [Transports](https://python-all.ru/3.14/library/asyncio-protocol.html#transports) описывает классы asyncio [`BaseTransport`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseTransport), [`ReadTransport`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.ReadTransport), [`WriteTransport`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport), [`Transport`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Transport), [`DatagramTransport`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.DatagramTransport) и [`SubprocessTransport`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.SubprocessTransport).34- Раздел [Protocols](https://python-all.ru/3.14/library/asyncio-protocol.html#protocols) описывает классы asyncio [`BaseProtocol`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol), [`Protocol`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Protocol), [`BufferedProtocol`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BufferedProtocol), [`DatagramProtocol`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.SubprocessProtocol).35- Раздел [Examples](https://python-all.ru/3.14/library/asyncio-protocol.html#examples) показывает, как работать с транспортами, протоколами и низкоуровневыми API цикла событий.3637## Транспорты3839**Исходный код:** [Lib/asyncio/transports.py](https://python-all.ru/src/3.14/Lib/asyncio/transports.py)4041---4243Транспорты – это классы, предоставляемые [`asyncio`](https://python-all.ru/3.14/library/asyncio.html#module-asyncio) для абстракции различных видов каналов связи.4445Объекты транспорта всегда создаются [циклом событий asyncio](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio-event-loop).4647asyncio реализует транспорты для TCP, UDP, SSL и каналов подпроцессов. Доступные методы транспорта зависят от его типа.4849Классы транспортов [не потокобезопасны](https://python-all.ru/3.14/library/asyncio-dev.html#asyncio-multithreading).5051### Иерархия транспортов5253#### `class asyncio.BaseTransport`5455Базовый класс для всех транспортов. Содержит методы, общие для всех транспортов asyncio.5657#### `class asyncio.WriteTransport(BaseTransport)`5859Базовый транспорт для соединений только на запись.6061Экземпляры класса *WriteTransport* возвращаются из метода цикла событий [`loop.connect_write_pipe()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).6263#### `class asyncio.ReadTransport(BaseTransport)`6465Базовый транспорт для соединений только на чтение.6667Экземпляры класса *ReadTransport* возвращаются из метода цикла событий [`loop.connect_read_pipe()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).6869#### `class asyncio.Transport(WriteTransport, ReadTransport)`7071Интерфейс, представляющий двунаправленный транспорт, такой как TCP-соединение.7273Пользователь не создаёт транспорт напрямую; он вызывает вспомогательную функцию, передавая фабрику протокола и другие сведения, необходимые для создания транспорта и протокола.7475Экземпляры класса *транспорт* возвращаются или используются методами цикла событий, например [`loop.create_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.create_server()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.sendfile()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.sendfile) и т.д.7677#### `class asyncio.DatagramTransport(BaseTransport)`7879Транспорт для дейтаграммных (UDP) соединений.8081Экземпляры класса *DatagramTransport* возвращаются из метода цикла событий [`loop.create_datagram_endpoint()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint).8283#### `class asyncio.SubprocessTransport(BaseTransport)`8485Абстракция, представляющая соединение между родительским и дочерним процессом ОС.8687Экземпляры класса *SubprocessTransport* возвращаются из методов цикла событий [`loop.subprocess_shell()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell) и [`loop.subprocess_exec()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).8889### Базовый транспорт9091#### `BaseTransport.close()`9293Закрывает транспорт.9495Если у транспорта есть буфер для исходящих данных, буферизированные данные будут асинхронно сброшены. Новые данные приниматься не будут. После сброса всех буферизированных данных будет вызван метод протокола [`protocol.connection_lost()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) с аргументом [`None`](https://python-all.ru/3.14/library/constants.html#None). После закрытия транспорт не следует использовать.9697#### `BaseTransport.is_closing()`9899Возвращает `True`, если транспорт закрывается или уже закрыт.100101#### `BaseTransport.get_extra_info(name, default=None)`102103Возвращает информацию о транспорте или используемых им базовых ресурсах.104105*name* – строка, представляющая запрашиваемую часть информации, специфичной для транспорта.106107*default* – значение, возвращаемое, если информация недоступна, либо если транспорт не поддерживает её запрос с данной сторонней реализацией цикла событий или на текущей платформе.108109Например, следующий код пытается получить базовый объект сокета транспорта:110111```python112sock = transport.get_extra_info('socket')113if sock is not None:114    print(sock.getsockopt(...))115```116117Категории информации, которые можно запросить для некоторых транспортов:118119- сокет:120121  - `'peername'`: удалённый адрес, к которому подключён сокет, результат [`socket.socket.getpeername()`](https://python-all.ru/3.14/library/socket.html#socket.socket.getpeername) (`None` в случае ошибки)122  - `'socket'`: экземпляр [`socket.socket`](https://python-all.ru/3.14/library/socket.html#socket.socket)123  - `'sockname'`: собственный адрес сокета, результат [`socket.socket.getsockname()`](https://python-all.ru/3.14/library/socket.html#socket.socket.getsockname)124- SSL-сокет:125126  - `'compression'`: используемый алгоритм сжатия в виде строки, или `None`, если соединение не сжато; результат [`ssl.SSLSocket.compression()`](https://python-all.ru/3.14/library/ssl.html#ssl.SSLSocket.compression)127  - `'cipher'`: кортеж из трёх значений, содержащий название используемого шифра, версию протокола SSL, определяющую его использование, и количество используемых секретных бит; результат [`ssl.SSLSocket.cipher()`](https://python-all.ru/3.14/library/ssl.html#ssl.SSLSocket.cipher)128  - `'peercert'`: сертификат однорангового узла; результат [`ssl.SSLSocket.getpeercert()`](https://python-all.ru/3.14/library/ssl.html#ssl.SSLSocket.getpeercert)129  - `'sslcontext'`: экземпляр [`ssl.SSLContext`](https://python-all.ru/3.14/library/ssl.html#ssl.SSLContext)130  - `'ssl_object'`: экземпляр [`ssl.SSLObject`](https://python-all.ru/3.14/library/ssl.html#ssl.SSLObject) или [`ssl.SSLSocket`](https://python-all.ru/3.14/library/ssl.html#ssl.SSLSocket)131- канал:132133  - `'pipe'`: объект канала134- подпроцесс:135136  - `'subprocess'`: экземпляр [`subprocess.Popen`](https://python-all.ru/3.14/library/subprocess.html#subprocess.Popen)137138#### `BaseTransport.set_protocol(protocol)`139140Устанавливает новый протокол.141142Переключать протокол следует только при условии, что оба протокола документированы как поддерживающие переключение.143144#### `BaseTransport.get_protocol()`145146Возвращает текущий протокол.147148### Транспорты только для чтения149150#### `ReadTransport.is_reading()`151152Возвращает `True`, если транспорт получает новые данные.153154Добавлено в версии 3.7.155156#### `ReadTransport.pause_reading()`157158Приостанавливает приёмную сторону транспорта. Данные не будут передаваться методу [`protocol.data_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Protocol.data_received) протокола до тех пор, пока не будет вызван [`resume_reading()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.ReadTransport.resume_reading).159160Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже приостановлен или закрыт.161162#### `ReadTransport.resume_reading()`163164Возобновляет приёмную сторону. Метод протокола [`protocol.data_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Protocol.data_received) будет вызван снова, если для чтения доступны какие-либо данные.165166Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже читает.167168### Транспорты только для записи169170#### `WriteTransport.abort()`171172Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод [`protocol.connection_lost()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.14/library/constants.html#None).173174#### `WriteTransport.can_write_eof()`175176Возвращает [`True`](https://python-all.ru/3.14/library/constants.html#True), если транспорт поддерживает [`write_eof()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), и [`False`](https://python-all.ru/3.14/library/constants.html#False) в противном случае.177178#### `WriteTransport.get_write_buffer_size()`179180Возвращает текущий размер выходного буфера, используемого транспортом.181182#### `WriteTransport.get_write_buffer_limits()`183184Get the *high* and *low* watermarks for write flow control. Return a tuple `(low, high)` where *low* and *high* are positive number of bytes.185186Используйте [`set_write_buffer_limits()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) для установки границ.187188Добавлено в версии 3.4.2.189190#### `WriteTransport.set_write_buffer_limits(high=None, low=None)`191192Устанавливает *верхнюю* и *нижнюю* границы для управления потоком записи.193194Эти два значения (измеряются в количестве байтов) управляют, когда методы протокола [`protocol.pause_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) и [`protocol.resume_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызываются. Если заданы, нижняя граница должна быть меньше или равна верхней. Ни *верхняя*, ни *нижняя* не могут быть отрицательными.195196[`pause_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) вызывается, когда размер буфера становится больше или равен *верхнему* значению. Если запись была приостановлена, [`resume_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера становится меньше или равен *нижнему* значению.197198Значения по умолчанию зависят от реализации. Если задан только верхний порог, нижний порог по умолчанию принимает зависящее от реализации значение, меньшее или равное верхнему. Установка *верхнего* порога в ноль приводит к тому, что *нижний* также становится нулевым, и [`pause_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) будет вызываться всякий раз, когда буфер становится непустым. Установка *нижнего* порога в ноль приводит к тому, что [`resume_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) будет вызываться только когда буфер опустеет. Использование нуля для любого порога в целом неоптимально, так как сокращает возможности для одновременного выполнения ввода-вывода и вычислений.199200[`get_write_buffer_limits()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport.get_write_buffer_limits) используется для получения ограничений.201202#### `WriteTransport.write(data)`203204Записывает несколько байт *данных* в транспорт.205206Этот метод не блокирует выполнение; он буферизует данные и обеспечивает их асинхронную отправку.207208#### `WriteTransport.writelines(list_of_data)`209210Записывает в транспорт список (или любой итерируемый объект) из байт данных. Функционально это эквивалентно вызову [`write()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport.write) для каждого элемента, полученного из итерируемого объекта, но может быть реализовано более эффективно.211212#### `WriteTransport.write_eof()`213214Закрывает записывающий конец транспорта после сброса всех буферизованных данных. Данные всё ещё могут приниматься.215216Этот метод может вызывать [`NotImplementedError`](https://python-all.ru/3.14/library/exceptions.html#NotImplementedError), если транспорт (например, SSL) не поддерживает полузакрытые соединения.217218### Дейтаграммные транспорты219220#### `DatagramTransport.sendto(data, addr=None)`221222Отправляет байты *данных* удаленному узлу, задаваемому *addr* (адрес назначения, зависящий от транспорта). Если *addr* равно [`None`](https://python-all.ru/3.14/library/constants.html#None), данные отправляются на адрес назначения, указанный при создании транспорта.223224Этот метод не блокирует выполнение; он буферизует данные и организует их асинхронную отправку.225226Изменено в версии 3.13: Этот метод можно вызывать с пустым объектом bytes для отправки дейтаграммы нулевой длины. Расчёт размера буфера, используемый для управления потоком, также обновлён с учётом заголовка дейтаграммы.227228#### `DatagramTransport.abort()`229230Немедленно закрывает транспорт, не дожидаясь завершения ожидающих операций. Буферизованные данные будут потеряны. Приём данных прекратится. Метод [`protocol.connection_lost()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с [`None`](https://python-all.ru/3.14/library/constants.html#None) в качестве аргумента.231232### Транспорты подпроцессов233234#### `SubprocessTransport.get_pid()`235236Возвращает идентификатор процесса подпроцесса в виде целого числа.237238#### `SubprocessTransport.get_pipe_transport(fd)`239240Возвращает транспорт для канала связи, соответствующего целочисленному файловому дескриптору *fd*:241242- `0`: потоковый транспорт для записи стандартного ввода (*stdin*), или [`None`](https://python-all.ru/3.14/library/constants.html#None), если подпроцесс не был создан с `stdin=PIPE`243- `1`: потоковый транспорт для чтения стандартного вывода (*stdout*), или [`None`](https://python-all.ru/3.14/library/constants.html#None), если подпроцесс не был создан с `stdout=PIPE`244- `2`: потоковый транспорт для чтения стандартного вывода ошибок (*stderr*), или [`None`](https://python-all.ru/3.14/library/constants.html#None), если подпроцесс не был создан с `stderr=PIPE`245- другой *fd*: [`None`](https://python-all.ru/3.14/library/constants.html#None)246247#### `SubprocessTransport.get_returncode()`248249Возвращает код возврата подпроцесса в виде целого числа или [`None`](https://python-all.ru/3.14/library/constants.html#None), если подпроцесс ещё не завершился, что аналогично атрибуту [`subprocess.Popen.returncode`](https://python-all.ru/3.14/library/subprocess.html#subprocess.Popen.returncode).250251#### `SubprocessTransport.kill()`252253Завершает подпроцесс.254255В системах POSIX функция отправляет подпроцессу сигнал SIGKILL. В Windows этот метод является псевдонимом для [`terminate()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.SubprocessTransport.terminate).256257См. также [`subprocess.Popen.kill()`](https://python-all.ru/3.14/library/subprocess.html#subprocess.Popen.kill).258259#### `SubprocessTransport.send_signal(signal)`260261Отправляет подпроцессу номер *signal*, как в [`subprocess.Popen.send_signal()`](https://python-all.ru/3.14/library/subprocess.html#subprocess.Popen.send_signal).262263#### `SubprocessTransport.terminate()`264265Останавливает подпроцесс.266267На системах POSIX этот метод отправляет [`SIGTERM`](https://python-all.ru/3.14/library/signal.html#signal.SIGTERM) подпроцессу. В Windows для остановки подпроцесса вызывается функция Windows API `TerminateProcess()`.268269См. также [`subprocess.Popen.terminate()`](https://python-all.ru/3.14/library/subprocess.html#subprocess.Popen.terminate).270271#### `SubprocessTransport.close()`272273Завершает подпроцесс вызовом метода [`kill()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.SubprocessTransport.kill).274275Если подпроцесс ещё не завершён, закрывает транспорты каналов *stdin*, *stdout* и *stderr*.276277## Протоколы278279**Исходный код:** [Lib/asyncio/protocols.py](https://python-all.ru/src/3.14/Lib/asyncio/protocols.py)280281---282283asyncio предоставляет набор абстрактных базовых классов, которые следует использовать для реализации сетевых протоколов. Они предназначены для использования вместе с [транспортами](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio-transport).284285Подклассы абстрактных базовых классов протоколов могут реализовывать часть методов или все. Все эти методы – колбэки: они вызываются транспортами при определённых событиях, например, при получении данных. Метод базового протокола должен вызываться соответствующим транспортом.286287### Базовые протоколы288289#### `class asyncio.BaseProtocol`290291Базовый протокол, содержащий методы, общие для всех протоколов.292293#### `class asyncio.Protocol(BaseProtocol)`294295Базовый класс для реализации потоковых протоколов (TCP, сокеты Unix и т.д.).296297#### `class asyncio.BufferedProtocol(BaseProtocol)`298299Базовый класс для реализации потоковых протоколов с ручным управлением буфером приёма.300301#### `class asyncio.DatagramProtocol(BaseProtocol)`302303Базовый класс для реализации дейтаграммных протоколов (UDP).304305#### `class asyncio.SubprocessProtocol(BaseProtocol)`306307Базовый класс для реализации протоколов, взаимодействующих с дочерними процессами (однонаправленные каналы).308309### Базовый протокол310311Все протоколы asyncio могут реализовывать колбэки базового протокола.312313Колбэки соединения314315Колбэки соединения вызываются для всех протоколов ровно один раз при каждом успешном соединении. Все остальные колбэки протокола могут вызываться только между этими двумя методами.316317#### `BaseProtocol.connection_made(transport)`318319Вызывается при установлении соединения.320321Аргумент *transport* – это транспорт, представляющий соединение. Протокол отвечает за хранение ссылки на свой транспорт.322323#### `BaseProtocol.connection_lost(exc)`324325Вызывается при потере или закрытии соединения.326327The argument is either an exception object or [`None`](https://python-all.ru/3.14/library/constants.html#None). The latter means a regular EOF is received, or the connection was aborted or closed by this side of the connection.328329Колбэки управления потоком330331Транспорты могут вызывать колбэки управления потоком, чтобы приостановить или возобновить запись, выполняемую протоколом.332333Подробнее см. в документации метода [`set_write_buffer_limits()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) .334335#### `BaseProtocol.pause_writing()`336337Вызывается, когда буфер транспорта превышает верхнюю границу.338339#### `BaseProtocol.resume_writing()`340341Вызывается, когда буфер транспорта опускается ниже нижней границы.342343Если размер буфера равен верхней границе, [`pause_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) не вызывается: размер буфера должен быть строго больше.344345И наоборот, [`resume_writing()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера равен нижней границе или меньше её. Эти граничные условия важны, чтобы всё работало ожидаемым образом, когда любая из границ равна нулю.346347### Потоковые протоколы348349Методы событий, такие как [`loop.create_server()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.create_unix_server()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_unix_server), [`loop.create_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.connect_accepted_socket()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.connect_accepted_socket), [`loop.connect_read_pipe()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe) и [`loop.connect_write_pipe()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), принимают фабрики, возвращающие потоковые протоколы.350351#### `Protocol.data_received(data)`352353Вызывается при получении данных. *data* – это непустой bytes объект, содержащий входящие данные.354355То, буферизуются ли данные, разбиваются на части или собираются заново, зависит от tранспорта. В общем случае не следует полагаться на конкретную семантику, а вместо этого делать парсинг общим и гибким. Однако данные всегда принимаются в правильном порядке.356357Метод может вызываться произвольное количество раз, пока соединение открыто.358359Однако [`protocol.eof_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза. Как только `eof_received()` вызывается, `data_received()` больше не вызывается.360361#### `Protocol.eof_received()`362363Вызывается, когда удалённая сторона сигнализирует, что больше не будет отправлять данные (например, вызовом [`transport.write_eof()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), если другая сторона также использует asyncio).364365Этот метод может вернуть ложное значение (включая `None`), и в этом случае транспорт закроется сам. И наоборот, если метод возвращает истинное значение, используемый протокол определяет, закрывать ли транспорт. Поскольку реализация по умолчанию возвращает `None`, она неявно закрывает соединение.366367Некоторые транспорты, включая SSL, не поддерживают полузакрытые соединения, и в этом случае возврат истинного значения из этого метода приведёт к закрытию соединения.368369Автомат состояний:370371```text372start -> connection_made373    [-> data_received]*374    [-> eof_received]?375-> connection_lost -> end376```377378### Буферизованные потоковые протоколы379380Добавлено в версии 3.7.381382Буферизованные протоколы могут использоваться с любым методом цикла событий, который поддерживает [потоковые протоколы](https://python-all.ru/3.14/library/asyncio-protocol.html#streaming-protocols).383384Реализации `BufferedProtocol` позволяют явное ручное выделение и управление буфером приёма. Циклы событий могут затем использовать буфер, предоставленный протоколом, чтобы избежать лишнего копирования данных. Это может привести к заметному повышению производительности для протоколов, которые получают большие объёмы данных. Сложные реализации протоколов могут значительно сократить количество выделений буфера.385386Следующие колбэки вызываются на экземплярах [`BufferedProtocol`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BufferedProtocol) :387388#### `BufferedProtocol.get_buffer(sizehint)`389390Вызывается для выделения нового буфера приёма.391392*sizehint* – это рекомендуемый минимальный размер для возвращаемого буфера. Допускается возвращать буферы меньше или больше, чем предлагает *sizehint*. Если установлено -1, размер буфера может быть произвольным. Возвращать буфер нулевого размера – ошибка.393394`get_buffer()` должен возвращать объект, реализующий [буферный протокол](https://python-all.ru/3.14/c-api/buffer.html#bufferobjects).395396#### `BufferedProtocol.buffer_updated(nbytes)`397398Вызывается, когда буфер был обновлён полученными данными.399400*nbytes* – общее количество байтов, записанных в буфер.401402#### `BufferedProtocol.eof_received()`403404См. документацию метода [`protocol.eof_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Protocol.eof_received).405406[`get_buffer()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BufferedProtocol.get_buffer) может вызываться произвольное число раз во время соединения. Однако [`protocol.eof_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза, и, если он вызван, `get_buffer()` и [`buffer_updated()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.BufferedProtocol.buffer_updated) не будут вызваны после него.407408Автомат состояний:409410```text411start -> connection_made412    [-> get_buffer413        [-> buffer_updated]?414    ]*415    [-> eof_received]?416-> connection_lost -> end417```418419### Протоколы дейтаграмм420421Экземпляры протокола дейтаграмм должны создаваться фабриками протоколов, передаваемыми методу [`loop.create_datagram_endpoint()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint).422423#### `DatagramProtocol.datagram_received(data, addr)`424425Вызывается при получении дейтаграммы. *data* – это байтовый объект, содержащий входящие данные. *addr* – адрес узла, отправляющего данные; точный формат зависит от транспорта.426427#### `DatagramProtocol.error_received(exc)`428429Вызывается, когда предыдущая операция отправки или получения вызывает исключение [`OSError`](https://python-all.ru/3.14/library/exceptions.html#OSError). *exc* – экземпляр `OSError`.430431Этот метод вызывается в редких случаях, когда транспорт (например, UDP) обнаруживает, что дейтаграмма не может быть доставлена получателю. Однако во многих ситуациях недоставленные дейтаграммы молча отбрасываются.432433> **Примечание**434>435> В системах BSD (macOS, FreeBSD и т.д.) управление потоком не поддерживается для протоколов дейтаграмм, поскольку нет надёжного способа обнаружить ошибки отправки, вызванные записью слишком большого количества пакетов.436>437> Сокет всегда выглядит «готовым», а избыточные пакеты отбрасываются. Исключение [`OSError`](https://python-all.ru/3.14/library/exceptions.html#OSError) с `errno`, установленным в [`errno.ENOBUFS`](https://python-all.ru/3.14/library/errno.html#errno.ENOBUFS), может быть или не быть возбуждено; если оно возбуждено, оно будет передано в [`DatagramProtocol.error_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.DatagramProtocol.error_received), но в остальных случаях игнорируется.438439### Протоколы подпроцессов440441Экземпляры протокола подпроцессов должны создаваться фабриками протоколов, передаваемыми методам [`loop.subprocess_exec()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec) и [`loop.subprocess_shell()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell).442443#### `SubprocessProtocol.pipe_data_received(fd, data)`444445Вызывается, когда дочерний процесс записывает данные в свой канал stdout или stderr.446447*fd* – целочисленный файловый дескриптор канала.448449*data* – непустой байтовый объект, содержащий полученные данные.450451#### `SubprocessProtocol.pipe_connection_lost(fd, exc)`452453Вызывается, когда один из каналов, связывающих с дочерним процессом, закрывается.454455*fd* – целочисленный файловый дескриптор, который был закрыт.456457#### `SubprocessProtocol.process_exited()`458459Вызывается, когда дочерний процесс завершился.460461Он может быть вызван до методов [`pipe_data_received()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.SubprocessProtocol.pipe_data_received) и [`pipe_connection_lost()`](https://python-all.ru/3.14/library/asyncio-protocol.html#asyncio.SubprocessProtocol.pipe_connection_lost).462463## Примеры464465### TCP-эхо-сервер466467Создайте TCP-эхо-сервер с помощью метода [`loop.create_server()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_server), отправьте обратно полученные данные и закройте соединение:468469```python470import asyncio471472class EchoServerProtocol(asyncio.Protocol):473    def connection_made(self, transport):474        peername = transport.get_extra_info('peername')475        print('Connection from {}'.format(peername))476        self.transport = transport477478    def data_received(self, data):479        message = data.decode()480        print('Data received: {!r}'.format(message))481482        print('Send: {!r}'.format(message))483        self.transport.write(data)484485        print('Close the client socket')486        self.transport.close()487488async def main():489    # Получаем ссылку на цикл событий, так как планируем использовать490    # низкоуровневые API.491    loop = asyncio.get_running_loop()492493    server = await loop.create_server(494        EchoServerProtocol,495        '127.0.0.1', 8888)496497    async with server:498        await server.serve_forever()499500asyncio.run(main())501```502503> **См. также**504>505> Пример [TCP-эхо-сервера с использованием потоков](https://python-all.ru/3.14/library/asyncio-stream.html#asyncio-tcp-echo-server-streams) использует высокоуровневую функцию [`asyncio.start_server()`](https://python-all.ru/3.14/library/asyncio-stream.html#asyncio.start_server).506507### TCP-эхо-клиент508509TCP-эхо-клиент, использующий метод [`loop.create_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_connection), отправляет данные и ждёт, пока соединение не будет закрыто:510511```python512import asyncio513514class EchoClientProtocol(asyncio.Protocol):515    def __init__(self, message, on_con_lost):516        self.message = message517        self.on_con_lost = on_con_lost518519    def connection_made(self, transport):520        transport.write(self.message.encode())521        print('Data sent: {!r}'.format(self.message))522523    def data_received(self, data):524        print('Data received: {!r}'.format(data.decode()))525526    def connection_lost(self, exc):527        print('The server closed the connection')528        self.on_con_lost.set_result(True)529530async def main():531    # Получаем ссылку на цикл событий, так как планируем использовать532    # низкоуровневые API.533    loop = asyncio.get_running_loop()534535    on_con_lost = loop.create_future()536    message = 'Hello World!'537538    transport, protocol = await loop.create_connection(539        lambda: EchoClientProtocol(message, on_con_lost),540        '127.0.0.1', 8888)541542    # Ожидаем, пока протокол не сообщит, что соединение543    # потеряно, и закрываем транспорт.544    try:545        await on_con_lost546    finally:547        transport.close()548549asyncio.run(main())550```551552> **См. также**553>554> Пример [TCP-эхо-клиента с использованием потоков](https://python-all.ru/3.14/library/asyncio-stream.html#asyncio-tcp-echo-client-streams) использует высокоуровневую функцию [`asyncio.open_connection()`](https://python-all.ru/3.14/library/asyncio-stream.html#asyncio.open_connection).555556### UDP-эхо-сервер557558UDP-эхо-сервер, использующий метод [`loop.create_datagram_endpoint()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint), отправляет обратно полученные данные:559560```python561import asyncio562563class EchoServerProtocol:564    def connection_made(self, transport):565        self.transport = transport566567    def datagram_received(self, data, addr):568        message = data.decode()569        print('Received %r from %s' % (message, addr))570        print('Send %r to %s' % (message, addr))571        self.transport.sendto(data, addr)572573async def main():574    print("Starting UDP server")575576    # Получаем ссылку на цикл событий, так как планируем использовать577    # низкоуровневые API.578    loop = asyncio.get_running_loop()579580    # Будет создан один экземпляр протокола для обслуживания всех581    # запросов клиентов.582    transport, protocol = await loop.create_datagram_endpoint(583        EchoServerProtocol,584        local_addr=('127.0.0.1', 9999))585586    try:587        await asyncio.sleep(3600)  # Обслуживать в течение 1 часа.588    finally:589        transport.close()590591asyncio.run(main())592```593594### UDP-эхо-клиент595596UDP-эхо-клиент с помощью метода [`loop.create_datagram_endpoint()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint) отправляет данные и закрывает транспорт при получении ответа:597598```python599import asyncio600601class EchoClientProtocol:602    def __init__(self, message, on_con_lost):603        self.message = message604        self.on_con_lost = on_con_lost605        self.transport = None606607    def connection_made(self, transport):608        self.transport = transport609        print('Send:', self.message)610        self.transport.sendto(self.message.encode())611612    def datagram_received(self, data, addr):613        print("Received:", data.decode())614615        print("Close the socket")616        self.transport.close()617618    def error_received(self, exc):619        print('Error received:', exc)620621    def connection_lost(self, exc):622        print("Connection closed")623        self.on_con_lost.set_result(True)624625async def main():626    # Получаем ссылку на цикл событий, так как планируем использовать627    # низкоуровневые API.628    loop = asyncio.get_running_loop()629630    on_con_lost = loop.create_future()631    message = "Hello World!"632633    transport, protocol = await loop.create_datagram_endpoint(634        lambda: EchoClientProtocol(message, on_con_lost),635        remote_addr=('127.0.0.1', 9999))636637    try:638        await on_con_lost639    finally:640        transport.close()641642asyncio.run(main())643```644645### Подключение существующих сокетов646647Ожидание получения данных сокетом с помощью метода [`loop.create_connection()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.create_connection) и протокола:648649```python650import asyncio651import socket652653class MyProtocol(asyncio.Protocol):654655    def __init__(self, on_con_lost):656        self.transport = None657        self.on_con_lost = on_con_lost658659    def connection_made(self, transport):660        self.transport = transport661662    def data_received(self, data):663        print("Received:", data.decode())664665        # Мы закончили: закрываем транспорт;666        # connection_lost() будет вызван автоматически.667        self.transport.close()668669    def connection_lost(self, exc):670        # Сокет был закрыт671        self.on_con_lost.set_result(True)672673async def main():674    # Получаем ссылку на цикл событий, так как планируем использовать675    # низкоуровневые API.676    loop = asyncio.get_running_loop()677    on_con_lost = loop.create_future()678679    # Создаём пару соединённых сокетов680    rsock, wsock = socket.socketpair()681682    # Регистрируем сокет для ожидания данных.683    transport, protocol = await loop.create_connection(684        lambda: MyProtocol(on_con_lost), sock=rsock)685686    # Имитируем приём данных из сети.687    loop.call_soon(wsock.send, 'abc'.encode())688689    try:690        await protocol.on_con_lost691    finally:692        transport.close()693        wsock.close()694695asyncio.run(main())696```697698> **См. также**699>700> В примере [наблюдения за файловым дескриптором на предмет событий чтения](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio-example-watch-fd) используется низкоуровневый метод [`loop.add_reader()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.add_reader) для регистрации FD.701>702> В примере [регистрации открытого сокета для ожидания данных с помощью потоков](https://python-all.ru/3.14/library/asyncio-stream.html#asyncio-example-create-connection-streams) используются высокоуровневые потоки , созданные функцией [`open_connection()`](https://python-all.ru/3.14/library/asyncio-stream.html#asyncio.open_connection) в корутине.703704### loop.subprocess\_exec() и SubprocessProtocol705706Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения.707708Подпроцесс создаётся методом [`loop.subprocess_exec()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec):709710```python711import asyncio712import sys713714class DateProtocol(asyncio.SubprocessProtocol):715    def __init__(self, exit_future):716        self.exit_future = exit_future717        self.output = bytearray()718        self.pipe_closed = False719        self.exited = False720721    def pipe_connection_lost(self, fd, exc):722        self.pipe_closed = True723        self.check_for_exit()724725    def pipe_data_received(self, fd, data):726        self.output.extend(data)727728    def process_exited(self):729        self.exited = True730        # Метод process_exited() может быть вызван до731        # метода pipe_connection_lost(): ждём, пока оба метода не будут732        # вызваны.733        self.check_for_exit()734735    def check_for_exit(self):736        if self.pipe_closed and self.exited:737            self.exit_future.set_result(True)738739async def get_date():740    # Получаем ссылку на цикл событий, так как планируем использовать741    # низкоуровневые API.742    loop = asyncio.get_running_loop()743744    code = 'import datetime as dt; print(dt.datetime.now())'745    exit_future = asyncio.Future(loop=loop)746747    # Создаём подпроцесс, управляемый DateProtocol;748    # перенаправляем стандартный вывод в канал.749    transport, protocol = await loop.subprocess_exec(750        lambda: DateProtocol(exit_future),751        sys.executable, '-c', code,752        stdin=None, stderr=None)753754    # Ожидаем завершения подпроцесса с помощью метода process_exited()755    # протокола.756    await exit_future757758    # Закрываем канал stdout.759    transport.close()760761    # Читаем вывод, который был собран методом762    # pipe_data_received() протокола.763    data = bytes(protocol.output)764    return data.decode('ascii').rstrip()765766date = asyncio.run(get_date())767print(f"Current date: {date}")768```769770См. также [тот же пример](https://python-all.ru/3.14/library/asyncio-subprocess.html#asyncio-example-create-subprocess-exec) , написанный с использованием высокоуровневых API.771