asyncio-protocol.md
1> **Источник:** https://python-all.ru/3.13/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.13/library/asyncio-eventloop.html#asyncio.loop.create_connection). Они используют стиль программирования на основе колбэков и позволяют создавать высокопроизводительные реализации сетевых протоколов и протоколов межпроцессного взаимодействия (например, HTTP).1213По существу, транспорты и протоколы следует использовать только в библиотеках и фреймворках, но никогда в высокоуровневых приложениях asyncio.1415Эта страница документации охватывает как [транспорты](https://python-all.ru/3.13/library/asyncio-protocol.html#transports), так и [протоколы](https://python-all.ru/3.13/library/asyncio-protocol.html#protocols).1617Введение1819На самом высоком уровне транспорт отвечает за *то, как* передаются байты, тогда как протокол определяет, *какие* байты передавать (и в некоторой степени – когда).2021Другими словами: транспорт – это абстракция для сокета (или аналогичной точки ввода-вывода), а протокол – это абстракция для приложения с точки зрения транспорта.2223Ещё один взгляд: интерфейсы транспорта и протокола вместе определяют абстрактный интерфейс для использования сетевого ввода-вывода и межпроцессного ввода-вывода.2425Между объектами транспорта и протокола всегда существует отношение 1:1: протокол вызывает методы транспорта для отправки данных, а транспорт вызывает методы протокола, чтобы передать ему полученные данные.2627Большинство методов цикла событий, ориентированных на соединения (таких как [`loop.create_connection()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_connection)), обычно принимают аргумент *protocol\_factory*, используемый для создания объекта *Protocol* для принятого соединения, представленного объектом *Transport*. Такие методы обычно возвращают кортеж `(transport, protocol)`.2829Содержание3031Эта страница документации содержит следующие разделы:3233- Раздел [Transports](https://python-all.ru/3.13/library/asyncio-protocol.html#transports) описывает классы asyncio [`BaseTransport`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseTransport), [`ReadTransport`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.ReadTransport), [`WriteTransport`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.WriteTransport), [`Transport`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.Transport), [`DatagramTransport`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.DatagramTransport) и [`SubprocessTransport`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.SubprocessTransport).34- Раздел [Protocols](https://python-all.ru/3.13/library/asyncio-protocol.html#protocols) описывает классы asyncio [`BaseProtocol`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol), [`Protocol`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.Protocol), [`BufferedProtocol`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BufferedProtocol), [`DatagramProtocol`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.SubprocessProtocol).35- Раздел [Examples](https://python-all.ru/3.13/library/asyncio-protocol.html#examples) показывает, как работать с транспортами, протоколами и низкоуровневыми API цикла событий.3637## Транспорты3839**Исходный код:** [Lib/asyncio/transports.py](https://python-all.ru/src/3.13/Lib/asyncio/transports.py)4041---4243Транспорты – это классы, предоставляемые [`asyncio`](https://python-all.ru/3.13/library/asyncio.html#module-asyncio) для абстракции различных видов каналов связи.4445Объекты транспорта всегда создаются [циклом событий asyncio](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio-event-loop).4647asyncio реализует транспорты для TCP, UDP, SSL и каналов подпроцессов. Доступные методы транспорта зависят от его типа.4849Классы транспортов [не потокобезопасны](https://python-all.ru/3.13/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.13/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.13/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.13/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.13/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.13/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.create_server()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.sendfile()`](https://python-all.ru/3.13/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.13/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.13/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell) и [`loop.subprocess_exec()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).8889### Базовый транспорт9091#### `BaseTransport.close()`9293Закрывает транспорт.9495Если у транспорта есть буфер для исходящих данных, буферизированные данные будут асинхронно сброшены. Новые данные приниматься не будут. После сброса всех буферизированных данных будет вызван метод протокола [`protocol.connection_lost()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) с аргументом [`None`](https://python-all.ru/3.13/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.13/library/socket.html#socket.socket.getpeername) (`None` в случае ошибки)122 - `'socket'`: экземпляр [`socket.socket`](https://python-all.ru/3.13/library/socket.html#socket.socket)123 - `'sockname'`: собственный адрес сокета, результат [`socket.socket.getsockname()`](https://python-all.ru/3.13/library/socket.html#socket.socket.getsockname)124- SSL-сокет:125126 - `'compression'`: используемый алгоритм сжатия в виде строки, или `None`, если соединение не сжато; результат [`ssl.SSLSocket.compression()`](https://python-all.ru/3.13/library/ssl.html#ssl.SSLSocket.compression)127 - `'cipher'`: кортеж из трёх значений, содержащий название используемого шифра, версию протокола SSL, определяющую его использование, и количество используемых секретных бит; результат [`ssl.SSLSocket.cipher()`](https://python-all.ru/3.13/library/ssl.html#ssl.SSLSocket.cipher)128 - `'peercert'`: сертификат однорангового узла; результат [`ssl.SSLSocket.getpeercert()`](https://python-all.ru/3.13/library/ssl.html#ssl.SSLSocket.getpeercert)129 - `'sslcontext'`: экземпляр [`ssl.SSLContext`](https://python-all.ru/3.13/library/ssl.html#ssl.SSLContext)130 - `'ssl_object'`: экземпляр [`ssl.SSLObject`](https://python-all.ru/3.13/library/ssl.html#ssl.SSLObject) или [`ssl.SSLSocket`](https://python-all.ru/3.13/library/ssl.html#ssl.SSLSocket)131- канал:132133 - `'pipe'`: объект канала134- подпроцесс:135136 - `'subprocess'`: экземпляр [`subprocess.Popen`](https://python-all.ru/3.13/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.13/library/asyncio-protocol.html#asyncio.Protocol.data_received) протокола до тех пор, пока не будет вызван [`resume_reading()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.ReadTransport.resume_reading).159160Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже приостановлен или закрыт.161162#### `ReadTransport.resume_reading()`163164Возобновляет приёмную сторону. Метод протокола [`protocol.data_received()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.Protocol.data_received) будет вызван снова, если для чтения доступны какие-либо данные.165166Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже читает.167168### Транспорты только для записи169170#### `WriteTransport.abort()`171172Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод [`protocol.connection_lost()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.13/library/constants.html#None).173174#### `WriteTransport.can_write_eof()`175176Возвращает [`True`](https://python-all.ru/3.13/library/constants.html#True), если транспорт поддерживает [`write_eof()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), и [`False`](https://python-all.ru/3.13/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.13/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.13/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) и [`protocol.resume_writing()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызываются. Если заданы, нижняя граница должна быть меньше или равна верхней. Ни *верхняя*, ни *нижняя* не могут быть отрицательными.195196[`pause_writing()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) вызывается, когда размер буфера становится больше или равен *верхнему* значению. Если запись была приостановлена, [`resume_writing()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера становится меньше или равен *нижнему* значению.197198Значения по умолчанию зависят от реализации. Если задан только верхний порог, нижний порог по умолчанию принимает зависящее от реализации значение, меньшее или равное верхнему. Установка *верхнего* порога в ноль приводит к тому, что *нижний* также становится нулевым, и [`pause_writing()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) будет вызываться всякий раз, когда буфер становится непустым. Установка *нижнего* порога в ноль приводит к тому, что [`resume_writing()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) будет вызываться только когда буфер опустеет. Использование нуля для любого порога в целом неоптимально, так как сокращает возможности для одновременного выполнения ввода-вывода и вычислений.199200[`get_write_buffer_limits()`](https://python-all.ru/3.13/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.13/library/asyncio-protocol.html#asyncio.WriteTransport.write) для каждого элемента, полученного из итерируемого объекта, но может быть реализовано более эффективно.211212#### `WriteTransport.write_eof()`213214Закрывает записывающий конец транспорта после сброса всех буферизованных данных. Данные всё ещё могут приниматься.215216Этот метод может вызывать [`NotImplementedError`](https://python-all.ru/3.13/library/exceptions.html#NotImplementedError), если транспорт (например, SSL) не поддерживает полузакрытые соединения.217218### Дейтаграммные транспорты219220#### `DatagramTransport.sendto(data, addr=None)`221222Отправляет байты *данных* удаленному узлу, задаваемому *addr* (адрес назначения, зависящий от транспорта). Если *addr* равно [`None`](https://python-all.ru/3.13/library/constants.html#None), данные отправляются на адрес назначения, указанный при создании транспорта.223224Этот метод не блокирует выполнение; он буферизует данные и организует их асинхронную отправку.225226Изменено в версии 3.13: Этот метод можно вызывать с пустым объектом bytes для отправки дейтаграммы нулевой длины. Расчёт размера буфера, используемый для управления потоком, также обновлён с учётом заголовка дейтаграммы.227228#### `DatagramTransport.abort()`229230Немедленно закрывает транспорт, не дожидаясь завершения ожидающих операций. Буферизованные данные будут потеряны. Приём данных прекратится. Метод [`protocol.connection_lost()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с [`None`](https://python-all.ru/3.13/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.13/library/constants.html#None), если подпроцесс не был создан с `stdin=PIPE`243- `1`: потоковый транспорт для чтения стандартного вывода (*stdout*), или [`None`](https://python-all.ru/3.13/library/constants.html#None), если подпроцесс не был создан с `stdout=PIPE`244- `2`: потоковый транспорт для чтения стандартного вывода ошибок (*stderr*), или [`None`](https://python-all.ru/3.13/library/constants.html#None), если подпроцесс не был создан с `stderr=PIPE`245- другой *fd*: [`None`](https://python-all.ru/3.13/library/constants.html#None)246247#### `SubprocessTransport.get_returncode()`248249Возвращает код возврата подпроцесса в виде целого числа или [`None`](https://python-all.ru/3.13/library/constants.html#None), если подпроцесс ещё не завершился, что аналогично атрибуту [`subprocess.Popen.returncode`](https://python-all.ru/3.13/library/subprocess.html#subprocess.Popen.returncode).250251#### `SubprocessTransport.kill()`252253Завершает подпроцесс.254255В системах POSIX функция отправляет подпроцессу сигнал SIGKILL. В Windows этот метод является псевдонимом для [`terminate()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.SubprocessTransport.terminate).256257См. также [`subprocess.Popen.kill()`](https://python-all.ru/3.13/library/subprocess.html#subprocess.Popen.kill).258259#### `SubprocessTransport.send_signal(signal)`260261Отправляет подпроцессу номер *signal*, как в [`subprocess.Popen.send_signal()`](https://python-all.ru/3.13/library/subprocess.html#subprocess.Popen.send_signal).262263#### `SubprocessTransport.terminate()`264265Останавливает подпроцесс.266267На системах POSIX этот метод отправляет [`SIGTERM`](https://python-all.ru/3.13/library/signal.html#signal.SIGTERM) подпроцессу. В Windows для остановки подпроцесса вызывается функция Windows API `TerminateProcess()`.268269См. также [`subprocess.Popen.terminate()`](https://python-all.ru/3.13/library/subprocess.html#subprocess.Popen.terminate).270271#### `SubprocessTransport.close()`272273Завершает подпроцесс вызовом метода [`kill()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.SubprocessTransport.kill).274275Если подпроцесс ещё не завершён, закрывает транспорты каналов *stdin*, *stdout* и *stderr*.276277## Протоколы278279**Исходный код:** [Lib/asyncio/protocols.py](https://python-all.ru/src/3.13/Lib/asyncio/protocols.py)280281---282283asyncio предоставляет набор абстрактных базовых классов, которые следует использовать для реализации сетевых протоколов. Они предназначены для использования вместе с [транспортами](https://python-all.ru/3.13/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.13/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.13/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.13/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) не вызывается: размер буфера должен быть строго больше.344345И наоборот, [`resume_writing()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера равен нижней границе или меньше её. Эти граничные условия важны, чтобы всё работало ожидаемым образом, когда любая из границ равна нулю.346347### Потоковые протоколы348349Методы событий, такие как [`loop.create_server()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.create_unix_server()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_unix_server), [`loop.create_connection()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.connect_accepted_socket()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.connect_accepted_socket), [`loop.connect_read_pipe()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe) и [`loop.connect_write_pipe()`](https://python-all.ru/3.13/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.13/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.13/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.13/library/asyncio-protocol.html#streaming-protocols).383384Реализации `BufferedProtocol` позволяют явное ручное выделение и управление буфером приёма. Циклы событий могут затем использовать буфер, предоставленный протоколом, чтобы избежать лишнего копирования данных. Это может привести к заметному повышению производительности для протоколов, которые получают большие объёмы данных. Сложные реализации протоколов могут значительно сократить количество выделений буфера.385386Следующие колбэки вызываются на экземплярах [`BufferedProtocol`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BufferedProtocol) :387388#### `BufferedProtocol.get_buffer(sizehint)`389390Вызывается для выделения нового буфера приёма.391392*sizehint* – это рекомендуемый минимальный размер для возвращаемого буфера. Допускается возвращать буферы меньше или больше, чем предлагает *sizehint*. Если установлено -1, размер буфера может быть произвольным. Возвращать буфер нулевого размера – ошибка.393394`get_buffer()` должен возвращать объект, реализующий [буферный протокол](https://python-all.ru/3.13/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.13/library/asyncio-protocol.html#asyncio.Protocol.eof_received).405406[`get_buffer()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.BufferedProtocol.get_buffer) может вызываться произвольное число раз во время соединения. Однако [`protocol.eof_received()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза, и, если он вызван, `get_buffer()` и [`buffer_updated()`](https://python-all.ru/3.13/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.13/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.13/library/exceptions.html#OSError). *exc* – экземпляр `OSError`.430431Этот метод вызывается в редких случаях, когда транспорт (например, UDP) обнаруживает, что дейтаграмма не может быть доставлена получателю. Однако во многих ситуациях недоставленные дейтаграммы молча отбрасываются.432433> **Примечание**434>435> В системах BSD (macOS, FreeBSD и т.д.) управление потоком не поддерживается для протоколов дейтаграмм, поскольку нет надёжного способа обнаружить ошибки отправки, вызванные записью слишком большого количества пакетов.436>437> Сокет всегда выглядит «готовым», а избыточные пакеты отбрасываются. Исключение [`OSError`](https://python-all.ru/3.13/library/exceptions.html#OSError) с `errno`, установленным в [`errno.ENOBUFS`](https://python-all.ru/3.13/library/errno.html#errno.ENOBUFS), может быть или не быть возбуждено; если оно возбуждено, оно будет передано в [`DatagramProtocol.error_received()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.DatagramProtocol.error_received), но в остальных случаях игнорируется.438439### Протоколы подпроцессов440441Экземпляры протокола подпроцессов должны создаваться фабриками протоколов, передаваемыми методам [`loop.subprocess_exec()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec) и [`loop.subprocess_shell()`](https://python-all.ru/3.13/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.13/library/asyncio-protocol.html#asyncio.SubprocessProtocol.pipe_data_received) и [`pipe_connection_lost()`](https://python-all.ru/3.13/library/asyncio-protocol.html#asyncio.SubprocessProtocol.pipe_connection_lost).462463## Примеры464465### TCP-эхо-сервер466467Создайте TCP-эхо-сервер с помощью метода [`loop.create_server()`](https://python-all.ru/3.13/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.13/library/asyncio-stream.html#asyncio-tcp-echo-server-streams) использует высокоуровневую функцию [`asyncio.start_server()`](https://python-all.ru/3.13/library/asyncio-stream.html#asyncio.start_server).506507### TCP-эхо-клиент508509TCP-эхо-клиент, использующий метод [`loop.create_connection()`](https://python-all.ru/3.13/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.13/library/asyncio-stream.html#asyncio-tcp-echo-client-streams) использует высокоуровневую функцию [`asyncio.open_connection()`](https://python-all.ru/3.13/library/asyncio-stream.html#asyncio.open_connection).555556### UDP-эхо-сервер557558UDP-эхо-сервер, использующий метод [`loop.create_datagram_endpoint()`](https://python-all.ru/3.13/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.13/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.13/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.13/library/asyncio-eventloop.html#asyncio-example-watch-fd) используется низкоуровневый метод [`loop.add_reader()`](https://python-all.ru/3.13/library/asyncio-eventloop.html#asyncio.loop.add_reader) для регистрации FD.701>702> В примере [регистрации открытого сокета для ожидания данных с помощью потоков](https://python-all.ru/3.13/library/asyncio-stream.html#asyncio-example-create-connection-streams) используются высокоуровневые потоки , созданные функцией [`open_connection()`](https://python-all.ru/3.13/library/asyncio-stream.html#asyncio.open_connection) в корутине.703704### loop.subprocess\_exec() и SubprocessProtocol705706Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения.707708Подпроцесс создаётся методом [`loop.subprocess_exec()`](https://python-all.ru/3.13/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.13/library/asyncio-subprocess.html#asyncio-example-create-subprocess-exec) , написанный с использованием высокоуровневых API.771