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

asyncio-protocol.md

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

1> **Источник:** https://python-all.ru/3.12/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.12/library/asyncio-eventloop.html#asyncio.loop.create_connection). Они используют стиль программирования на основе колбэков и позволяют создавать высокопроизводительные реализации сетевых протоколов и протоколов межпроцессного взаимодействия (например, HTTP).1213По существу, транспорты и протоколы следует использовать только в библиотеках и фреймворках, но никогда в высокоуровневых приложениях asyncio.1415Эта страница документации охватывает как [транспорты](https://python-all.ru/3.12/library/asyncio-protocol.html#transports), так и [протоколы](https://python-all.ru/3.12/library/asyncio-protocol.html#protocols).1617Введение1819На самом высоком уровне транспорт отвечает за *то, как* передаются байты, тогда как протокол определяет, *какие* байты передавать (и в некоторой степени – когда).2021Другими словами: транспорт – это абстракция для сокета (или аналогичной точки ввода-вывода), а протокол – это абстракция для приложения с точки зрения транспорта.2223Ещё один взгляд: интерфейсы транспорта и протокола вместе определяют абстрактный интерфейс для использования сетевого ввода-вывода и межпроцессного ввода-вывода.2425Между объектами транспорта и протокола всегда существует отношение 1:1: протокол вызывает методы транспорта для отправки данных, а транспорт вызывает методы протокола, чтобы передать ему полученные данные.2627Большинство методов цикла событий, ориентированных на соединения (таких как [`loop.create_connection()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_connection)), обычно принимают аргумент *protocol\_factory*, используемый для создания объекта *Protocol* для принятого соединения, представленного объектом *Transport*. Такие методы обычно возвращают кортеж `(transport, protocol)`.2829Содержание3031Эта страница документации содержит следующие разделы:3233- Раздел [Transports](https://python-all.ru/3.12/library/asyncio-protocol.html#transports) описывает классы asyncio [`BaseTransport`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseTransport), [`ReadTransport`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.ReadTransport), [`WriteTransport`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.WriteTransport), [`Transport`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.Transport), [`DatagramTransport`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.DatagramTransport) и [`SubprocessTransport`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.SubprocessTransport).34- Раздел [Protocols](https://python-all.ru/3.12/library/asyncio-protocol.html#protocols) описывает классы asyncio [`BaseProtocol`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol), [`Protocol`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.Protocol), [`BufferedProtocol`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BufferedProtocol), [`DatagramProtocol`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.SubprocessProtocol).35- Раздел [Examples](https://python-all.ru/3.12/library/asyncio-protocol.html#examples) показывает, как работать с транспортами, протоколами и низкоуровневыми API цикла событий.3637## Транспорты3839**Исходный код:** [Lib/asyncio/transports.py](https://python-all.ru/src/3.12/Lib/asyncio/transports.py)4041---4243Транспорты – это классы, предоставляемые [`asyncio`](https://python-all.ru/3.12/library/asyncio.html#module-asyncio) для абстракции различных видов каналов связи.4445Объекты транспорта всегда создаются [циклом событий asyncio](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio-event-loop).4647asyncio реализует транспорты для TCP, UDP, SSL и каналов подпроцессов. Доступные методы транспорта зависят от его типа.4849Классы транспортов [не потокобезопасны](https://python-all.ru/3.12/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.12/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.12/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.12/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.12/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.12/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.create_server()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.sendfile()`](https://python-all.ru/3.12/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.12/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.12/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell) и [`loop.subprocess_exec()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).8889### Базовый транспорт9091#### `BaseTransport.close()`9293Закрывает транспорт.9495Если у транспорта есть буфер для исходящих данных, буферизированные данные будут асинхронно сброшены. Новые данные приниматься не будут. После сброса всех буферизированных данных будет вызван метод протокола [`protocol.connection_lost()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) с аргументом [`None`](https://python-all.ru/3.12/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.12/library/socket.html#socket.socket.getpeername) (`None` в случае ошибки)122  - `'socket'`: экземпляр [`socket.socket`](https://python-all.ru/3.12/library/socket.html#socket.socket)123  - `'sockname'`: собственный адрес сокета, результат [`socket.socket.getsockname()`](https://python-all.ru/3.12/library/socket.html#socket.socket.getsockname)124- SSL-сокет:125126  - `'compression'`: используемый алгоритм сжатия в виде строки, или `None`, если соединение не сжато; результат [`ssl.SSLSocket.compression()`](https://python-all.ru/3.12/library/ssl.html#ssl.SSLSocket.compression)127  - `'cipher'`: кортеж из трёх значений, содержащий название используемого шифра, версию протокола SSL, определяющую его использование, и количество используемых секретных бит; результат [`ssl.SSLSocket.cipher()`](https://python-all.ru/3.12/library/ssl.html#ssl.SSLSocket.cipher)128  - `'peercert'`: сертификат однорангового узла; результат [`ssl.SSLSocket.getpeercert()`](https://python-all.ru/3.12/library/ssl.html#ssl.SSLSocket.getpeercert)129  - `'sslcontext'`: экземпляр [`ssl.SSLContext`](https://python-all.ru/3.12/library/ssl.html#ssl.SSLContext)130  - `'ssl_object'`: экземпляр [`ssl.SSLObject`](https://python-all.ru/3.12/library/ssl.html#ssl.SSLObject) или [`ssl.SSLSocket`](https://python-all.ru/3.12/library/ssl.html#ssl.SSLSocket)131- канал:132133  - `'pipe'`: объект канала134- подпроцесс:135136  - `'subprocess'`: экземпляр [`subprocess.Popen`](https://python-all.ru/3.12/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.12/library/asyncio-protocol.html#asyncio.Protocol.data_received) протокола до тех пор, пока не будет вызван [`resume_reading()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.ReadTransport.resume_reading).159160Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже приостановлен или закрыт.161162#### `ReadTransport.resume_reading()`163164Возобновляет приёмную сторону. Метод протокола [`protocol.data_received()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.Protocol.data_received) будет вызван снова, если для чтения доступны какие-либо данные.165166Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже читает.167168### Транспорты только для записи169170#### `WriteTransport.abort()`171172Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод [`protocol.connection_lost()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.12/library/constants.html#None).173174#### `WriteTransport.can_write_eof()`175176Возвращает [`True`](https://python-all.ru/3.12/library/constants.html#True), если транспорт поддерживает [`write_eof()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), и [`False`](https://python-all.ru/3.12/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.12/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.12/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) и [`protocol.resume_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызываются. Если заданы, нижняя граница должна быть меньше или равна верхней. Ни *верхняя*, ни *нижняя* не могут быть отрицательными.195196[`pause_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) вызывается, когда размер буфера становится больше или равен *верхнему* значению. Если запись была приостановлена, [`resume_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера становится меньше или равен *нижнему* значению.197198Значения по умолчанию зависят от реализации. Если задан только верхний порог, нижний порог по умолчанию принимает зависящее от реализации значение, меньшее или равное верхнему. Установка *верхнего* порога в ноль приводит к тому, что *нижний* также становится нулевым, и [`pause_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) будет вызываться всякий раз, когда буфер становится непустым. Установка *нижнего* порога в ноль приводит к тому, что [`resume_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) будет вызываться только когда буфер опустеет. Использование нуля для любого порога в целом неоптимально, так как сокращает возможности для одновременного выполнения ввода-вывода и вычислений.199200[`get_write_buffer_limits()`](https://python-all.ru/3.12/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.12/library/asyncio-protocol.html#asyncio.WriteTransport.write) для каждого элемента, полученного из итерируемого объекта, но может быть реализовано более эффективно.211212#### `WriteTransport.write_eof()`213214Закрывает записывающий конец транспорта после сброса всех буферизованных данных. Данные всё ещё могут приниматься.215216Этот метод может вызывать [`NotImplementedError`](https://python-all.ru/3.12/library/exceptions.html#NotImplementedError), если транспорт (например, SSL) не поддерживает полузакрытые соединения.217218### Дейтаграммные транспорты219220#### `DatagramTransport.sendto(data, addr=None)`221222Отправляет байты *данных* удаленному узлу, задаваемому *addr* (адрес назначения, зависящий от транспорта). Если *addr* равно [`None`](https://python-all.ru/3.12/library/constants.html#None), данные отправляются на адрес назначения, указанный при создании транспорта.223224Этот метод не блокирует выполнение; он буферизует данные и организует их асинхронную отправку.225226#### `DatagramTransport.abort()`227228Немедленно закрывает транспорт, не дожидаясь завершения ожидающих операций. Буферизованные данные будут потеряны. Приём данных прекратится. Метод [`protocol.connection_lost()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с [`None`](https://python-all.ru/3.12/library/constants.html#None) в качестве аргумента.229230### Транспорты подпроцессов231232#### `SubprocessTransport.get_pid()`233234Возвращает идентификатор процесса подпроцесса в виде целого числа.235236#### `SubprocessTransport.get_pipe_transport(fd)`237238Возвращает транспорт для канала связи, соответствующего целочисленному файловому дескриптору *fd*:239240- `0`: читаемый потоковый транспорт стандартного ввода (*stdin*) или [`None`](https://python-all.ru/3.12/library/constants.html#None), если подпроцесс был создан без `stdin=PIPE`241- `1`: записываемый потоковый транспорт стандартного вывода (*stdout*) или [`None`](https://python-all.ru/3.12/library/constants.html#None), если подпроцесс был создан без `stdout=PIPE`242- `2`: записываемый потоковый транспорт стандартного вывода ошибок (*stderr*) или [`None`](https://python-all.ru/3.12/library/constants.html#None), если подпроцесс был создан без `stderr=PIPE`243- другой *fd*: [`None`](https://python-all.ru/3.12/library/constants.html#None)244245#### `SubprocessTransport.get_returncode()`246247Возвращает код возврата подпроцесса в виде целого числа или [`None`](https://python-all.ru/3.12/library/constants.html#None), если подпроцесс ещё не завершился, что аналогично атрибуту [`subprocess.Popen.returncode`](https://python-all.ru/3.12/library/subprocess.html#subprocess.Popen.returncode).248249#### `SubprocessTransport.kill()`250251Завершает подпроцесс.252253В системах POSIX функция отправляет подпроцессу сигнал SIGKILL. В Windows этот метод является псевдонимом для [`terminate()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.SubprocessTransport.terminate).254255См. также [`subprocess.Popen.kill()`](https://python-all.ru/3.12/library/subprocess.html#subprocess.Popen.kill).256257#### `SubprocessTransport.send_signal(signal)`258259Отправляет подпроцессу номер *signal*, как в [`subprocess.Popen.send_signal()`](https://python-all.ru/3.12/library/subprocess.html#subprocess.Popen.send_signal).260261#### `SubprocessTransport.terminate()`262263Останавливает подпроцесс.264265На системах POSIX этот метод отправляет [`SIGTERM`](https://python-all.ru/3.12/library/signal.html#signal.SIGTERM) подпроцессу. В Windows для остановки подпроцесса вызывается функция Windows API `TerminateProcess()`.266267См. также [`subprocess.Popen.terminate()`](https://python-all.ru/3.12/library/subprocess.html#subprocess.Popen.terminate).268269#### `SubprocessTransport.close()`270271Завершает подпроцесс вызовом метода [`kill()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.SubprocessTransport.kill).272273Если подпроцесс ещё не завершён, закрывает транспорты каналов *stdin*, *stdout* и *stderr*.274275## Протоколы276277**Исходный код:** [Lib/asyncio/protocols.py](https://python-all.ru/src/3.12/Lib/asyncio/protocols.py)278279---280281asyncio предоставляет набор абстрактных базовых классов, которые следует использовать для реализации сетевых протоколов. Они предназначены для использования вместе с [транспортами](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio-transport).282283Подклассы абстрактных базовых классов протоколов могут реализовывать часть методов или все. Все эти методы – колбэки: они вызываются транспортами при определённых событиях, например, при получении данных. Метод базового протокола должен вызываться соответствующим транспортом.284285### Базовые протоколы286287#### `class asyncio.BaseProtocol`288289Базовый протокол, содержащий методы, общие для всех протоколов.290291#### `class asyncio.Protocol(BaseProtocol)`292293Базовый класс для реализации потоковых протоколов (TCP, сокеты Unix и т.д.).294295#### `class asyncio.BufferedProtocol(BaseProtocol)`296297Базовый класс для реализации потоковых протоколов с ручным управлением буфером приёма.298299#### `class asyncio.DatagramProtocol(BaseProtocol)`300301Базовый класс для реализации дейтаграммных протоколов (UDP).302303#### `class asyncio.SubprocessProtocol(BaseProtocol)`304305Базовый класс для реализации протоколов, взаимодействующих с дочерними процессами (однонаправленные каналы).306307### Базовый протокол308309Все протоколы asyncio могут реализовывать колбэки базового протокола.310311Колбэки соединения312313Колбэки соединения вызываются для всех протоколов ровно один раз при каждом успешном соединении. Все остальные колбэки протокола могут вызываться только между этими двумя методами.314315#### `BaseProtocol.connection_made(transport)`316317Вызывается при установлении соединения.318319Аргумент *transport* – это транспорт, представляющий соединение. Протокол отвечает за хранение ссылки на свой транспорт.320321#### `BaseProtocol.connection_lost(exc)`322323Вызывается при потере или закрытии соединения.324325The argument is either an exception object or [`None`](https://python-all.ru/3.12/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.326327Колбэки управления потоком328329Транспорты могут вызывать колбэки управления потоком, чтобы приостановить или возобновить запись, выполняемую протоколом.330331Подробнее см. в документации метода [`set_write_buffer_limits()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) .332333#### `BaseProtocol.pause_writing()`334335Вызывается, когда буфер транспорта превышает верхнюю границу.336337#### `BaseProtocol.resume_writing()`338339Вызывается, когда буфер транспорта опускается ниже нижней границы.340341Если размер буфера равен верхней границе, [`pause_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) не вызывается: размер буфера должен быть строго больше.342343И наоборот, [`resume_writing()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера равен нижней границе или меньше её. Эти граничные условия важны, чтобы всё работало ожидаемым образом, когда любая из границ равна нулю.344345### Потоковые протоколы346347Методы событий, такие как [`loop.create_server()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.create_unix_server()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_unix_server), [`loop.create_connection()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.connect_accepted_socket()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.connect_accepted_socket), [`loop.connect_read_pipe()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe) и [`loop.connect_write_pipe()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), принимают фабрики, возвращающие потоковые протоколы.348349#### `Protocol.data_received(data)`350351Вызывается при получении данных. *data* – это непустой bytes объект, содержащий входящие данные.352353То, буферизуются ли данные, разбиваются на части или собираются заново, зависит от tранспорта. В общем случае не следует полагаться на конкретную семантику, а вместо этого делать парсинг общим и гибким. Однако данные всегда принимаются в правильном порядке.354355Метод может вызываться произвольное количество раз, пока соединение открыто.356357Однако [`protocol.eof_received()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза. Как только `eof_received()` вызывается, `data_received()` больше не вызывается.358359#### `Protocol.eof_received()`360361Вызывается, когда удалённая сторона сигнализирует, что больше не будет отправлять данные (например, вызовом [`transport.write_eof()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), если другая сторона также использует asyncio).362363Этот метод может вернуть ложное значение (включая `None`), и в этом случае транспорт закроется сам. И наоборот, если метод возвращает истинное значение, используемый протокол определяет, закрывать ли транспорт. Поскольку реализация по умолчанию возвращает `None`, она неявно закрывает соединение.364365Некоторые транспорты, включая SSL, не поддерживают полузакрытые соединения, и в этом случае возврат истинного значения из этого метода приведёт к закрытию соединения.366367Автомат состояний:368369```text370start -> connection_made371    [-> data_received]*372    [-> eof_received]?373-> connection_lost -> end374```375376### Буферизованные потоковые протоколы377378Добавлено в версии 3.7.379380Буферизованные протоколы могут использоваться с любым методом цикла событий, который поддерживает [потоковые протоколы](https://python-all.ru/3.12/library/asyncio-protocol.html#streaming-protocols).381382Реализации `BufferedProtocol` позволяют явное ручное выделение и управление буфером приёма. Циклы событий могут затем использовать буфер, предоставленный протоколом, чтобы избежать лишнего копирования данных. Это может привести к заметному повышению производительности для протоколов, которые получают большие объёмы данных. Сложные реализации протоколов могут значительно сократить количество выделений буфера.383384Следующие колбэки вызываются на экземплярах [`BufferedProtocol`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BufferedProtocol) :385386#### `BufferedProtocol.get_buffer(sizehint)`387388Вызывается для выделения нового буфера приёма.389390*sizehint* – это рекомендуемый минимальный размер для возвращаемого буфера. Допускается возвращать буферы меньше или больше, чем предлагает *sizehint*. Если установлено -1, размер буфера может быть произвольным. Возвращать буфер нулевого размера – ошибка.391392`get_buffer()` должен возвращать объект, реализующий [буферный протокол](https://python-all.ru/3.12/c-api/buffer.html#bufferobjects).393394#### `BufferedProtocol.buffer_updated(nbytes)`395396Вызывается, когда буфер был обновлён полученными данными.397398*nbytes* – общее количество байтов, записанных в буфер.399400#### `BufferedProtocol.eof_received()`401402См. документацию метода [`protocol.eof_received()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.Protocol.eof_received).403404[`get_buffer()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BufferedProtocol.get_buffer) может вызываться произвольное число раз во время соединения. Однако [`protocol.eof_received()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза, и, если он вызван, [`get_buffer()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BufferedProtocol.get_buffer) и [`buffer_updated()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.BufferedProtocol.buffer_updated) не будут вызваны после него.405406Автомат состояний:407408```text409start -> connection_made410    [-> get_buffer411        [-> buffer_updated]?412    ]*413    [-> eof_received]?414-> connection_lost -> end415```416417### Протоколы дейтаграмм418419Экземпляры протокола дейтаграмм должны создаваться фабриками протоколов, передаваемыми методу [`loop.create_datagram_endpoint()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint).420421#### `DatagramProtocol.datagram_received(data, addr)`422423Вызывается при получении дейтаграммы. *data* – это байтовый объект, содержащий входящие данные. *addr* – адрес узла, отправляющего данные; точный формат зависит от транспорта.424425#### `DatagramProtocol.error_received(exc)`426427Вызывается, когда предыдущая операция отправки или получения вызывает исключение [`OSError`](https://python-all.ru/3.12/library/exceptions.html#OSError). *exc* – экземпляр [`OSError`](https://python-all.ru/3.12/library/exceptions.html#OSError).428429Этот метод вызывается в редких случаях, когда транспорт (например, UDP) обнаруживает, что дейтаграмма не может быть доставлена получателю. Однако во многих ситуациях недоставленные дейтаграммы молча отбрасываются.430431> **Примечание**432>433> В системах BSD (macOS, FreeBSD и т.д.) управление потоком не поддерживается для протоколов дейтаграмм, поскольку нет надёжного способа обнаружить ошибки отправки, вызванные записью слишком большого количества пакетов.434>435> Сокет всегда выглядит «готовым», а избыточные пакеты отбрасываются. Исключение [`OSError`](https://python-all.ru/3.12/library/exceptions.html#OSError) с `errno`, установленным в [`errno.ENOBUFS`](https://python-all.ru/3.12/library/errno.html#errno.ENOBUFS), может быть или не быть возбуждено; если оно возбуждено, оно будет передано в [`DatagramProtocol.error_received()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.DatagramProtocol.error_received), но в остальных случаях игнорируется.436437### Протоколы подпроцессов438439Экземпляры протокола подпроцессов должны создаваться фабриками протоколов, передаваемыми методам [`loop.subprocess_exec()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec) и [`loop.subprocess_shell()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell).440441#### `SubprocessProtocol.pipe_data_received(fd, data)`442443Вызывается, когда дочерний процесс записывает данные в свой канал stdout или stderr.444445*fd* – целочисленный файловый дескриптор канала.446447*data* – непустой байтовый объект, содержащий полученные данные.448449#### `SubprocessProtocol.pipe_connection_lost(fd, exc)`450451Вызывается, когда один из каналов, связывающих с дочерним процессом, закрывается.452453*fd* – целочисленный файловый дескриптор, который был закрыт.454455#### `SubprocessProtocol.process_exited()`456457Вызывается, когда дочерний процесс завершился.458459Он может быть вызван до методов [`pipe_data_received()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.SubprocessProtocol.pipe_data_received) и [`pipe_connection_lost()`](https://python-all.ru/3.12/library/asyncio-protocol.html#asyncio.SubprocessProtocol.pipe_connection_lost).460461## Примеры462463### TCP-эхо-сервер464465Создайте TCP-эхо-сервер с помощью метода [`loop.create_server()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_server), отправьте обратно полученные данные и закройте соединение:466467```python468import asyncio469470class EchoServerProtocol(asyncio.Protocol):471    def connection_made(self, transport):472        peername = transport.get_extra_info('peername')473        print('Connection from {}'.format(peername))474        self.transport = transport475476    def data_received(self, data):477        message = data.decode()478        print('Data received: {!r}'.format(message))479480        print('Send: {!r}'.format(message))481        self.transport.write(data)482483        print('Close the client socket')484        self.transport.close()485486async def main():487    # Получаем ссылку на цикл событий, так как планируем использовать488    # низкоуровневые API.489    loop = asyncio.get_running_loop()490491    server = await loop.create_server(492        lambda: EchoServerProtocol(),493        '127.0.0.1', 8888)494495    async with server:496        await server.serve_forever()497498asyncio.run(main())499```500501> **См. также**502>503> Пример [TCP-эхо-сервера с использованием потоков](https://python-all.ru/3.12/library/asyncio-stream.html#asyncio-tcp-echo-server-streams) использует высокоуровневую функцию [`asyncio.start_server()`](https://python-all.ru/3.12/library/asyncio-stream.html#asyncio.start_server).504505### TCP-эхо-клиент506507TCP-эхо-клиент, использующий метод [`loop.create_connection()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_connection), отправляет данные и ждёт, пока соединение не будет закрыто:508509```python510import asyncio511512class EchoClientProtocol(asyncio.Protocol):513    def __init__(self, message, on_con_lost):514        self.message = message515        self.on_con_lost = on_con_lost516517    def connection_made(self, transport):518        transport.write(self.message.encode())519        print('Data sent: {!r}'.format(self.message))520521    def data_received(self, data):522        print('Data received: {!r}'.format(data.decode()))523524    def connection_lost(self, exc):525        print('The server closed the connection')526        self.on_con_lost.set_result(True)527528async def main():529    # Получаем ссылку на цикл событий, так как планируем использовать530    # низкоуровневые API.531    loop = asyncio.get_running_loop()532533    on_con_lost = loop.create_future()534    message = 'Hello World!'535536    transport, protocol = await loop.create_connection(537        lambda: EchoClientProtocol(message, on_con_lost),538        '127.0.0.1', 8888)539540    # Ожидаем, пока протокол не сообщит, что соединение541    # потеряно, и закрываем транспорт.542    try:543        await on_con_lost544    finally:545        transport.close()546547asyncio.run(main())548```549550> **См. также**551>552> Пример [TCP-эхо-клиента с использованием потоков](https://python-all.ru/3.12/library/asyncio-stream.html#asyncio-tcp-echo-client-streams) использует высокоуровневую функцию [`asyncio.open_connection()`](https://python-all.ru/3.12/library/asyncio-stream.html#asyncio.open_connection).553554### UDP-эхо-сервер555556UDP-эхо-сервер, использующий метод [`loop.create_datagram_endpoint()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint), отправляет обратно полученные данные:557558```python559import asyncio560561class EchoServerProtocol:562    def connection_made(self, transport):563        self.transport = transport564565    def datagram_received(self, data, addr):566        message = data.decode()567        print('Received %r from %s' % (message, addr))568        print('Send %r to %s' % (message, addr))569        self.transport.sendto(data, addr)570571async def main():572    print("Starting UDP server")573574    # Получаем ссылку на цикл событий, так как планируем использовать575    # низкоуровневые API.576    loop = asyncio.get_running_loop()577578    # Будет создан один экземпляр протокола для обслуживания всех579    # запросов клиентов.580    transport, protocol = await loop.create_datagram_endpoint(581        lambda: EchoServerProtocol(),582        local_addr=('127.0.0.1', 9999))583584    try:585        await asyncio.sleep(3600)  # Обслуживать в течение 1 часа.586    finally:587        transport.close()588589asyncio.run(main())590```591592### UDP-эхо-клиент593594UDP-эхо-клиент с помощью метода [`loop.create_datagram_endpoint()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint) отправляет данные и закрывает транспорт при получении ответа:595596```python597import asyncio598599class EchoClientProtocol:600    def __init__(self, message, on_con_lost):601        self.message = message602        self.on_con_lost = on_con_lost603        self.transport = None604605    def connection_made(self, transport):606        self.transport = transport607        print('Send:', self.message)608        self.transport.sendto(self.message.encode())609610    def datagram_received(self, data, addr):611        print("Received:", data.decode())612613        print("Close the socket")614        self.transport.close()615616    def error_received(self, exc):617        print('Error received:', exc)618619    def connection_lost(self, exc):620        print("Connection closed")621        self.on_con_lost.set_result(True)622623async def main():624    # Получаем ссылку на цикл событий, так как планируем использовать625    # низкоуровневые API.626    loop = asyncio.get_running_loop()627628    on_con_lost = loop.create_future()629    message = "Hello World!"630631    transport, protocol = await loop.create_datagram_endpoint(632        lambda: EchoClientProtocol(message, on_con_lost),633        remote_addr=('127.0.0.1', 9999))634635    try:636        await on_con_lost637    finally:638        transport.close()639640asyncio.run(main())641```642643### Подключение существующих сокетов644645Ожидание получения данных сокетом с помощью метода [`loop.create_connection()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.create_connection) и протокола:646647```python648import asyncio649import socket650651class MyProtocol(asyncio.Protocol):652653    def __init__(self, on_con_lost):654        self.transport = None655        self.on_con_lost = on_con_lost656657    def connection_made(self, transport):658        self.transport = transport659660    def data_received(self, data):661        print("Received:", data.decode())662663        # Мы закончили: закрываем транспорт;664        # connection_lost() будет вызван автоматически.665        self.transport.close()666667    def connection_lost(self, exc):668        # Сокет был закрыт669        self.on_con_lost.set_result(True)670671async def main():672    # Получаем ссылку на цикл событий, так как планируем использовать673    # низкоуровневые API.674    loop = asyncio.get_running_loop()675    on_con_lost = loop.create_future()676677    # Создаём пару соединённых сокетов678    rsock, wsock = socket.socketpair()679680    # Регистрируем сокет для ожидания данных.681    transport, protocol = await loop.create_connection(682        lambda: MyProtocol(on_con_lost), sock=rsock)683684    # Имитируем приём данных из сети.685    loop.call_soon(wsock.send, 'abc'.encode())686687    try:688        await protocol.on_con_lost689    finally:690        transport.close()691        wsock.close()692693asyncio.run(main())694```695696> **См. также**697>698> В примере [наблюдения за файловым дескриптором на предмет событий чтения](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio-example-watch-fd) используется низкоуровневый метод [`loop.add_reader()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.add_reader) для регистрации FD.699>700> В примере [регистрации открытого сокета для ожидания данных с помощью потоков](https://python-all.ru/3.12/library/asyncio-stream.html#asyncio-example-create-connection-streams) используются высокоуровневые потоки , созданные функцией [`open_connection()`](https://python-all.ru/3.12/library/asyncio-stream.html#asyncio.open_connection) в корутине.701702### loop.subprocess\_exec() и SubprocessProtocol703704Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения.705706Подпроцесс создаётся методом [`loop.subprocess_exec()`](https://python-all.ru/3.12/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec):707708```python709import asyncio710import sys711712class DateProtocol(asyncio.SubprocessProtocol):713    def __init__(self, exit_future):714        self.exit_future = exit_future715        self.output = bytearray()716        self.pipe_closed = False717        self.exited = False718719    def pipe_connection_lost(self, fd, exc):720        self.pipe_closed = True721        self.check_for_exit()722723    def pipe_data_received(self, fd, data):724        self.output.extend(data)725726    def process_exited(self):727        self.exited = True728        # Метод process_exited() может быть вызван до729        # метода pipe_connection_lost(): ждём, пока оба метода не будут730        # вызваны.731        self.check_for_exit()732733    def check_for_exit(self):734        if self.pipe_closed and self.exited:735            self.exit_future.set_result(True)736737async def get_date():738    # Получаем ссылку на цикл событий, так как планируем использовать739    # низкоуровневые API.740    loop = asyncio.get_running_loop()741742    code = 'import datetime; print(datetime.datetime.now())'743    exit_future = asyncio.Future(loop=loop)744745    # Создаём подпроцесс, управляемый DateProtocol;746    # перенаправляем стандартный вывод в канал.747    transport, protocol = await loop.subprocess_exec(748        lambda: DateProtocol(exit_future),749        sys.executable, '-c', code,750        stdin=None, stderr=None)751752    # Ожидаем завершения подпроцесса с помощью метода process_exited()753    # протокола.754    await exit_future755756    # Закрываем канал stdout.757    transport.close()758759    # Читаем вывод, который был собран методом760    # pipe_data_received() протокола.761    data = bytes(protocol.output)762    return data.decode('ascii').rstrip()763764date = asyncio.run(get_date())765print(f"Current date: {date}")766```767768См. также [тот же пример](https://python-all.ru/3.12/library/asyncio-subprocess.html#asyncio-example-create-subprocess-exec) , написанный с использованием высокоуровневых API.769