asyncio-protocol.md · Markdown – Документация Python
Документация Python неофициальный перевод

asyncio-protocol.md

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

1> **Источник:** https://python-all.ru/3.7/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.7/library/asyncio-eventloop.html#asyncio.loop.create_connection). Они используют стиль программирования на основе колбэков и позволяют создавать высокопроизводительные реализации сетевых протоколов и протоколов межпроцессного взаимодействия (например, HTTP).1213По существу, транспорты и протоколы следует использовать только в библиотеках и фреймворках, но никогда в высокоуровневых приложениях asyncio.1415Эта страница документации охватывает как [транспорты](https://python-all.ru/3.7/library/asyncio-protocol.html#transports), так и [протоколы](https://python-all.ru/3.7/library/asyncio-protocol.html#protocols).1617Введение1819На самом высоком уровне транспорт отвечает за *то, как* передаются байты, тогда как протокол определяет, *какие* байты передавать (и в некоторой степени – когда).2021Другими словами: транспорт – это абстракция для сокета (или аналогичной точки ввода-вывода), а протокол – это абстракция для приложения с точки зрения транспорта.2223Ещё один взгляд: интерфейсы транспорта и протокола вместе определяют абстрактный интерфейс для использования сетевого ввода-вывода и межпроцессного ввода-вывода.2425Между объектами транспорта и протокола всегда существует отношение 1:1: протокол вызывает методы транспорта для отправки данных, а транспорт вызывает методы протокола, чтобы передать ему полученные данные.2627Большинство методов цикла событий, ориентированных на соединения (таких как [`loop.create_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_connection)), обычно принимают аргумент *protocol\_factory*, используемый для создания объекта *Protocol* для принятого соединения, представленного объектом *Transport*. Такие методы обычно возвращают кортеж `(transport, protocol)`.2829Содержание3031Эта страница документации содержит следующие разделы:3233- Раздел [Transports](https://python-all.ru/3.7/library/asyncio-protocol.html#transports) описывает классы asyncio [`BaseTransport`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseTransport), [`ReadTransport`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.ReadTransport), [`WriteTransport`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport), [`Transport`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Transport), [`DatagramTransport`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.DatagramTransport) и [`SubprocessTransport`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.SubprocessTransport).34- Раздел [Protocols](https://python-all.ru/3.7/library/asyncio-protocol.html#protocols) описывает классы asyncio [`BaseProtocol`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol), [`Protocol`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Protocol), [`BufferedProtocol`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BufferedProtocol), [`DatagramProtocol`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.SubprocessProtocol).35- Раздел [Examples](https://python-all.ru/3.7/library/asyncio-protocol.html#examples) показывает, как работать с транспортами, протоколами и низкоуровневыми API цикла событий.3637## Транспорты3839Транспорты – это классы, предоставляемые [`asyncio`](https://python-all.ru/3.7/library/asyncio.html#module-asyncio) для абстракции различных видов каналов связи.4041Объекты транспорта всегда создаются [циклом событий asyncio](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio-event-loop).4243asyncio реализует транспорты для TCP, UDP, SSL и каналов подпроцессов. Доступные методы транспорта зависят от его типа.4445Классы транспортов [не потокобезопасны](https://python-all.ru/3.7/library/asyncio-dev.html#asyncio-multithreading).4647### Иерархия транспортов4849#### `class asyncio.BaseTransport`5051Базовый класс для всех транспортов. Содержит методы, общие для всех транспортов asyncio.5253#### `class asyncio.WriteTransport(BaseTransport)`5455Базовый транспорт для соединений только на запись.5657Экземпляры класса *WriteTransport* возвращаются из метода цикла событий [`loop.connect_write_pipe()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).5859#### `class asyncio.ReadTransport(BaseTransport)`6061Базовый транспорт для соединений только на чтение.6263Экземпляры класса *ReadTransport* возвращаются из метода цикла событий [`loop.connect_read_pipe()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe), а также используются методами, связанными с подпроцессами, такими как [`loop.subprocess_exec()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).6465#### `class asyncio.Transport(WriteTransport, ReadTransport)`6667Интерфейс, представляющий двунаправленный транспорт, такой как TCP-соединение.6869Пользователь не создаёт транспорт напрямую; он вызывает вспомогательную функцию, передавая фабрику протокола и другие сведения, необходимые для создания транспорта и протокола.7071Экземпляры класса *транспорт* возвращаются или используются методами цикла событий, например [`loop.create_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.create_server()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.sendfile()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.sendfile) и т.д.7273#### `class asyncio.DatagramTransport(BaseTransport)`7475Транспорт для дейтаграммных (UDP) соединений.7677Экземпляры класса *DatagramTransport* возвращаются из метода цикла событий [`loop.create_datagram_endpoint()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint).7879#### `class asyncio.SubprocessTransport(BaseTransport)`8081Абстракция, представляющая соединение между родительским и дочерним процессом ОС.8283Экземпляры класса *SubprocessTransport* возвращаются из методов цикла событий [`loop.subprocess_shell()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell) и [`loop.subprocess_exec()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec).8485### Базовый транспорт8687#### `BaseTransport.close()`8889Закрывает транспорт.9091Если у транспорта есть буфер для исходящих данных, буферизованные данные будут сброшены асинхронно. Новые данные приниматься не будут. После того как все буферизованные данные будут сброшены, метод [`protocol.connection_lost()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола будет вызван с [`None`](https://python-all.ru/3.7/library/constants.html#None) в качестве аргумента.9293#### `BaseTransport.is_closing()`9495Возвращает `True`, если транспорт закрывается или уже закрыт.9697#### `BaseTransport.get_extra_info(name, default=None)`9899Возвращает информацию о транспорте или используемых им базовых ресурсах.100101*name* – строка, представляющая запрашиваемую часть информации, специфичной для транспорта.102103*default* – значение, возвращаемое, если информация недоступна, либо если транспорт не поддерживает её запрос с данной сторонней реализацией цикла событий или на текущей платформе.104105Например, следующий код пытается получить базовый объект сокета транспорта:106107```python108sock = transport.get_extra_info('socket')109if sock is not None:110    print(sock.getsockopt(...))111```112113Категории информации, которые можно запросить для некоторых транспортов:114115- сокет:116117  - `'peername'`: удалённый адрес, к которому подключён сокет, результат [`socket.socket.getpeername()`](https://python-all.ru/3.7/library/socket.html#socket.socket.getpeername) (`None` в случае ошибки)118  - `'socket'`: экземпляр [`socket.socket`](https://python-all.ru/3.7/library/socket.html#socket.socket)119  - `'sockname'`: собственный адрес сокета, результат [`socket.socket.getsockname()`](https://python-all.ru/3.7/library/socket.html#socket.socket.getsockname)120- SSL-сокет:121122  - `'compression'`: используемый алгоритм сжатия в виде строки, или `None`, если соединение не сжато; результат [`ssl.SSLSocket.compression()`](https://python-all.ru/3.7/library/ssl.html#ssl.SSLSocket.compression)123  - `'cipher'`: кортеж из трёх значений, содержащий название используемого шифра, версию протокола SSL, определяющую его использование, и количество используемых секретных бит; результат [`ssl.SSLSocket.cipher()`](https://python-all.ru/3.7/library/ssl.html#ssl.SSLSocket.cipher)124  - `'peercert'`: сертификат однорангового узла; результат [`ssl.SSLSocket.getpeercert()`](https://python-all.ru/3.7/library/ssl.html#ssl.SSLSocket.getpeercert)125  - `'sslcontext'`: экземпляр [`ssl.SSLContext`](https://python-all.ru/3.7/library/ssl.html#ssl.SSLContext)126  - `'ssl_object'`: экземпляр [`ssl.SSLObject`](https://python-all.ru/3.7/library/ssl.html#ssl.SSLObject) или [`ssl.SSLSocket`](https://python-all.ru/3.7/library/ssl.html#ssl.SSLSocket)127- канал:128129  - `'pipe'`: объект канала130- подпроцесс:131132  - `'subprocess'`: экземпляр [`subprocess.Popen`](https://python-all.ru/3.7/library/subprocess.html#subprocess.Popen)133134#### `BaseTransport.set_protocol(protocol)`135136Устанавливает новый протокол.137138Переключать протокол следует только при условии, что оба протокола документированы как поддерживающие переключение.139140#### `BaseTransport.get_protocol()`141142Возвращает текущий протокол.143144### Транспорты только для чтения145146#### `ReadTransport.is_reading()`147148Возвращает `True`, если транспорт получает новые данные.149150Добавлено в версии 3.7.151152#### `ReadTransport.pause_reading()`153154Приостанавливает приёмную сторону транспорта. Данные не будут передаваться методу [`protocol.data_received()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Protocol.data_received) протокола до тех пор, пока не будет вызван [`resume_reading()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.ReadTransport.resume_reading).155156Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже приостановлен или закрыт.157158#### `ReadTransport.resume_reading()`159160Возобновляет приёмную сторону. Метод протокола [`protocol.data_received()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Protocol.data_received) будет вызван снова, если для чтения доступны какие-либо данные.161162Изменено в версии 3.7: Метод идемпотентен, то есть его можно вызывать, когда транспорт уже читает.163164### Транспорты только для записи165166#### `WriteTransport.abort()`167168Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод [`protocol.connection_lost()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.7/library/constants.html#None).169170#### `WriteTransport.can_write_eof()`171172Возвращает [`True`](https://python-all.ru/3.7/library/constants.html#True), если транспорт поддерживает [`write_eof()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), и [`False`](https://python-all.ru/3.7/library/constants.html#False) в противном случае.173174#### `WriteTransport.get_write_buffer_size()`175176Возвращает текущий размер выходного буфера, используемого транспортом.177178#### `WriteTransport.get_write_buffer_limits()`179180Get the *high* and *low* watermarks for write flow control. Return a tuple `(low, high)` where *low* and *high* are positive number of bytes.181182Используйте [`set_write_buffer_limits()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) для установки границ.183184Новое в версии 3.4.2.185186#### `WriteTransport.set_write_buffer_limits(high=None, low=None)`187188Устанавливает *верхнюю* и *нижнюю* границы для управления потоком записи.189190Эти два значения (измеряются в количестве байтов) управляют, когда методы протокола [`protocol.pause_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) и [`protocol.resume_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызываются. Если заданы, нижняя граница должна быть меньше или равна верхней. Ни *верхняя*, ни *нижняя* не могут быть отрицательными.191192[`pause_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) вызывается, когда размер буфера становится больше или равен *верхнему* значению. Если запись была приостановлена, [`resume_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера становится меньше или равен *нижнему* значению.193194Значения по умолчанию зависят от реализации. Если задан только верхний порог, нижний порог по умолчанию принимает зависящее от реализации значение, меньшее или равное верхнему. Установка *верхнего* порога в ноль приводит к тому, что *нижний* также становится нулевым, и [`pause_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) будет вызываться всякий раз, когда буфер становится непустым. Установка *нижнего* порога в ноль приводит к тому, что [`resume_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) будет вызываться только когда буфер опустеет. Использование нуля для любого порога в целом неоптимально, так как сокращает возможности для одновременного выполнения ввода-вывода и вычислений.195196[`get_write_buffer_limits()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport.get_write_buffer_limits) используется для получения ограничений.197198#### `WriteTransport.write(data)`199200Записывает несколько байт *данных* в транспорт.201202Этот метод не блокирует выполнение; он буферизует данные и обеспечивает их асинхронную отправку.203204#### `WriteTransport.writelines(list_of_data)`205206Записывает в транспорт список (или любой итерируемый объект) из байт данных. Функционально это эквивалентно вызову [`write()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport.write) для каждого элемента, полученного из итерируемого объекта, но может быть реализовано более эффективно.207208#### `WriteTransport.write_eof()`209210Закрывает записывающий конец транспорта после сброса всех буферизованных данных. Данные всё ещё могут приниматься.211212Этот метод может вызывать [`NotImplementedError`](https://python-all.ru/3.7/library/exceptions.html#NotImplementedError), если транспорт (например, SSL) не поддерживает полузакрытые соединения.213214### Дейтаграммные транспорты215216#### `DatagramTransport.sendto(data, addr=None)`217218Отправляет байты *данных* удаленному узлу, задаваемому *addr* (адрес назначения, зависящий от транспорта). Если *addr* равно [`None`](https://python-all.ru/3.7/library/constants.html#None), данные отправляются на адрес назначения, указанный при создании транспорта.219220Этот метод не блокирует выполнение; он буферизует данные и организует их асинхронную отправку.221222#### `DatagramTransport.abort()`223224Немедленно закрывает транспорт, не дожидаясь завершения ожидающих операций. Буферизованные данные будут потеряны. Приём данных прекратится. Метод [`protocol.connection_lost()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) протокола в конечном итоге будет вызван с [`None`](https://python-all.ru/3.7/library/constants.html#None) в качестве аргумента.225226### Транспорты подпроцессов227228#### `SubprocessTransport.get_pid()`229230Возвращает идентификатор процесса подпроцесса в виде целого числа.231232#### `SubprocessTransport.get_pipe_transport(fd)`233234Возвращает транспорт для канала связи, соответствующего целочисленному файловому дескриптору *fd*:235236- `0`: читаемый потоковый транспорт стандартного ввода (*stdin*) или [`None`](https://python-all.ru/3.7/library/constants.html#None), если подпроцесс был создан без `stdin=PIPE`237- `1`: записываемый потоковый транспорт стандартного вывода (*stdout*) или [`None`](https://python-all.ru/3.7/library/constants.html#None), если подпроцесс был создан без `stdout=PIPE`238- `2`: записываемый потоковый транспорт стандартного вывода ошибок (*stderr*) или [`None`](https://python-all.ru/3.7/library/constants.html#None), если подпроцесс был создан без `stderr=PIPE`239- другой *fd*: [`None`](https://python-all.ru/3.7/library/constants.html#None)240241#### `SubprocessTransport.get_returncode()`242243Возвращает код возврата подпроцесса в виде целого числа или [`None`](https://python-all.ru/3.7/library/constants.html#None), если подпроцесс ещё не завершился, что аналогично атрибуту [`subprocess.Popen.returncode`](https://python-all.ru/3.7/library/subprocess.html#subprocess.Popen.returncode).244245#### `SubprocessTransport.kill()`246247Завершает подпроцесс.248249В системах POSIX функция отправляет подпроцессу сигнал SIGKILL. В Windows этот метод является псевдонимом для [`terminate()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.SubprocessTransport.terminate).250251См. также [`subprocess.Popen.kill()`](https://python-all.ru/3.7/library/subprocess.html#subprocess.Popen.kill).252253#### `SubprocessTransport.send_signal(signal)`254255Отправляет подпроцессу номер *signal*, как в [`subprocess.Popen.send_signal()`](https://python-all.ru/3.7/library/subprocess.html#subprocess.Popen.send_signal).256257#### `SubprocessTransport.terminate()`258259Останавливает подпроцесс.260261На системах POSIX этот метод отправляет сигнал SIGTERM подпроцессу. В Windows вызывается функция Windows API TerminateProcess() для остановки подпроцесса.262263См. также [`subprocess.Popen.terminate()`](https://python-all.ru/3.7/library/subprocess.html#subprocess.Popen.terminate).264265#### `SubprocessTransport.close()`266267Завершает подпроцесс вызовом метода [`kill()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.SubprocessTransport.kill).268269Если подпроцесс ещё не завершён, закрывает транспорты каналов *stdin*, *stdout* и *stderr*.270271## Протоколы272273asyncio предоставляет набор абстрактных базовых классов, которые следует использовать для реализации сетевых протоколов. Они предназначены для использования вместе с [транспортами](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio-transport).274275Подклассы абстрактных базовых классов протоколов могут реализовывать часть методов или все. Все эти методы – колбэки: они вызываются транспортами при определённых событиях, например, при получении данных. Метод базового протокола должен вызываться соответствующим транспортом.276277### Базовые протоколы278279#### `class asyncio.BaseProtocol`280281Базовый протокол, содержащий методы, общие для всех протоколов.282283#### `class asyncio.Protocol(BaseProtocol)`284285Базовый класс для реализации потоковых протоколов (TCP, сокеты Unix и т.д.).286287#### `class asyncio.BufferedProtocol(BaseProtocol)`288289Базовый класс для реализации потоковых протоколов с ручным управлением буфером приёма.290291#### `class asyncio.DatagramProtocol(BaseProtocol)`292293Базовый класс для реализации дейтаграммных протоколов (UDP).294295#### `class asyncio.SubprocessProtocol(BaseProtocol)`296297Базовый класс для реализации протоколов, взаимодействующих с дочерними процессами (однонаправленные каналы).298299### Базовый протокол300301Все протоколы asyncio могут реализовывать колбэки базового протокола.302303Колбэки соединения304305Колбэки соединения вызываются для всех протоколов ровно один раз при каждом успешном соединении. Все остальные колбэки протокола могут вызываться только между этими двумя методами.306307#### `BaseProtocol.connection_made(transport)`308309Вызывается при установлении соединения.310311Аргумент *transport* – это транспорт, представляющий соединение. Протокол отвечает за хранение ссылки на свой транспорт.312313#### `BaseProtocol.connection_lost(exc)`314315Вызывается при потере или закрытии соединения.316317The argument is either an exception object or [`None`](https://python-all.ru/3.7/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.318319Колбэки управления потоком320321Транспорты могут вызывать колбэки управления потоком, чтобы приостановить или возобновить запись, выполняемую протоколом.322323Подробнее см. в документации метода [`set_write_buffer_limits()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) .324325#### `BaseProtocol.pause_writing()`326327Вызывается, когда буфер транспорта превышает верхнюю границу.328329#### `BaseProtocol.resume_writing()`330331Вызывается, когда буфер транспорта опускается ниже нижней границы.332333Если размер буфера равен верхней границе, [`pause_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.pause_writing) не вызывается: размер буфера должен быть строго больше.334335И наоборот, [`resume_writing()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BaseProtocol.resume_writing) вызывается, когда размер буфера равен нижней границе или меньше её. Эти граничные условия важны, чтобы всё работало ожидаемым образом, когда любая из границ равна нулю.336337### Потоковые протоколы338339Методы событий, такие как [`loop.create_server()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_server), [`loop.create_unix_server()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_unix_server), [`loop.create_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_connection), [`loop.create_unix_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_unix_connection), [`loop.connect_accepted_socket()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.connect_accepted_socket), [`loop.connect_read_pipe()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.connect_read_pipe) и [`loop.connect_write_pipe()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.connect_write_pipe), принимают фабрики, возвращающие потоковые протоколы.340341#### `Protocol.data_received(data)`342343Вызывается при получении данных. *data* – это непустой bytes объект, содержащий входящие данные.344345То, буферизуются ли данные, разбиваются на части или собираются заново, зависит от tранспорта. В общем случае не следует полагаться на конкретную семантику, а вместо этого делать парсинг общим и гибким. Однако данные всегда принимаются в правильном порядке.346347Метод может вызываться произвольное количество раз, пока соединение открыто.348349Однако [`protocol.eof_received()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза. После вызова *eof\_received()*, `data_received()` больше не вызывается.350351#### `Protocol.eof_received()`352353Вызывается, когда удалённая сторона сигнализирует, что больше не будет отправлять данные (например, вызовом [`transport.write_eof()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), если другая сторона также использует asyncio).354355Этот метод может вернуть ложное значение (включая `None`), и в этом случае транспорт закроется сам. И наоборот, если метод возвращает истинное значение, используемый протокол определяет, закрывать ли транспорт. Поскольку реализация по умолчанию возвращает `None`, она неявно закрывает соединение.356357Некоторые транспорты, включая SSL, не поддерживают полузакрытые соединения, и в этом случае возврат истинного значения из этого метода приведёт к закрытию соединения.358359Автомат состояний:360361```text362start -> connection_made363    [-> data_received]*364    [-> eof_received]?365-> connection_lost -> end366```367368### Буферизованные потоковые протоколы369370Новое в версии 3.7: **Важно!** это добавлено в asyncio в Python 3.7 *на временной основе*! Это экспериментальный API, который может быть изменён или полностью удалён в Python 3.8.371372Буферизованные протоколы могут использоваться с любым методом цикла событий, который поддерживает [потоковые протоколы](https://python-all.ru/3.7/library/asyncio-protocol.html#streaming-protocols).373374Реализации `BufferedProtocol` позволяют явное ручное выделение и управление буфером приёма. Циклы событий могут затем использовать буфер, предоставленный протоколом, чтобы избежать лишнего копирования данных. Это может привести к заметному повышению производительности для протоколов, которые получают большие объёмы данных. Сложные реализации протоколов могут значительно сократить количество выделений буфера.375376Следующие колбэки вызываются на экземплярах [`BufferedProtocol`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BufferedProtocol) :377378#### `BufferedProtocol.get_buffer(sizehint)`379380Вызывается для выделения нового буфера приёма.381382*sizehint* – это рекомендуемый минимальный размер для возвращаемого буфера. Допускается возвращать буферы меньше или больше, чем предлагает *sizehint*. Если установлено -1, размер буфера может быть произвольным. Возвращать буфер нулевого размера – ошибка.383384`get_buffer()` должен возвращать объект, реализующий [буферный протокол](https://python-all.ru/3.7/c-api/buffer.html#bufferobjects).385386#### `BufferedProtocol.buffer_updated(nbytes)`387388Вызывается, когда буфер был обновлён полученными данными.389390*nbytes* – общее количество байтов, записанных в буфер.391392#### `BufferedProtocol.eof_received()`393394См. документацию метода [`protocol.eof_received()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Protocol.eof_received).395396[`get_buffer()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BufferedProtocol.get_buffer) может вызываться произвольное число раз во время соединения. Однако [`protocol.eof_received()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.Protocol.eof_received) вызывается не более одного раза, и, если он вызван, [`get_buffer()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BufferedProtocol.get_buffer) и [`buffer_updated()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.BufferedProtocol.buffer_updated) не будут вызваны после него.397398Автомат состояний:399400```text401start -> connection_made402    [-> get_buffer403        [-> buffer_updated]?404    ]*405    [-> eof_received]?406-> connection_lost -> end407```408409### Протоколы дейтаграмм410411Экземпляры протокола дейтаграмм должны создаваться фабриками протоколов, передаваемыми методу [`loop.create_datagram_endpoint()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint).412413#### `DatagramProtocol.datagram_received(data, addr)`414415Вызывается при получении дейтаграммы. *data* – это байтовый объект, содержащий входящие данные. *addr* – адрес узла, отправляющего данные; точный формат зависит от транспорта.416417#### `DatagramProtocol.error_received(exc)`418419Вызывается, когда предыдущая операция отправки или получения вызывает исключение [`OSError`](https://python-all.ru/3.7/library/exceptions.html#OSError). *exc* – экземпляр [`OSError`](https://python-all.ru/3.7/library/exceptions.html#OSError).420421Этот метод вызывается в редких случаях, когда транспорт (например, UDP) обнаруживает, что дейтаграмма не может быть доставлена получателю. Однако во многих ситуациях недоставленные дейтаграммы молча отбрасываются.422423> **Примечание**424>425> В системах BSD (macOS, FreeBSD и т.д.) управление потоком не поддерживается для протоколов дейтаграмм, поскольку нет надёжного способа обнаружить ошибки отправки, вызванные записью слишком большого количества пакетов.426>427> Сокет всегда выглядит «готовым», а избыточные пакеты отбрасываются. Исключение [`OSError`](https://python-all.ru/3.7/library/exceptions.html#OSError) с `errno`, установленным в [`errno.ENOBUFS`](https://python-all.ru/3.7/library/errno.html#errno.ENOBUFS), может быть или не быть возбуждено; если оно возбуждено, оно будет передано в [`DatagramProtocol.error_received()`](https://python-all.ru/3.7/library/asyncio-protocol.html#asyncio.DatagramProtocol.error_received), но в остальных случаях игнорируется.428429### Протоколы подпроцессов430431Экземпляры Datagram Protocol должны создаваться фабриками протоколов, переданными в методы [`loop.subprocess_exec()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec) и [`loop.subprocess_shell()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell).432433#### `SubprocessProtocol.pipe_data_received(fd, data)`434435Вызывается, когда дочерний процесс записывает данные в свой канал stdout или stderr.436437*fd* – целочисленный файловый дескриптор канала.438439*data* – непустой байтовый объект, содержащий полученные данные.440441#### `SubprocessProtocol.pipe_connection_lost(fd, exc)`442443Вызывается, когда один из каналов, связывающих с дочерним процессом, закрывается.444445*fd* – целочисленный файловый дескриптор, который был закрыт.446447#### `SubprocessProtocol.process_exited()`448449Вызывается, когда дочерний процесс завершился.450451## Примеры452453### TCP-эхо-сервер454455Создайте TCP-эхо-сервер с помощью метода [`loop.create_server()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_server), отправьте обратно полученные данные и закройте соединение:456457```python458import asyncio459460class EchoServerProtocol(asyncio.Protocol):461    def connection_made(self, transport):462        peername = transport.get_extra_info('peername')463        print('Connection from {}'.format(peername))464        self.transport = transport465466    def data_received(self, data):467        message = data.decode()468        print('Data received: {!r}'.format(message))469470        print('Send: {!r}'.format(message))471        self.transport.write(data)472473        print('Close the client socket')474        self.transport.close()475476async def main():477    # Получаем ссылку на цикл событий, так как планируем использовать478    # низкоуровневые API.479    loop = asyncio.get_running_loop()480481    server = await loop.create_server(482        lambda: EchoServerProtocol(),483        '127.0.0.1', 8888)484485    async with server:486        await server.serve_forever()487488asyncio.run(main())489```490491> **См. также**492>493> Пример [TCP-эхо-сервера с использованием потоков](https://python-all.ru/3.7/library/asyncio-stream.html#asyncio-tcp-echo-server-streams) использует высокоуровневую функцию [`asyncio.start_server()`](https://python-all.ru/3.7/library/asyncio-stream.html#asyncio.start_server).494495### TCP-эхо-клиент496497TCP-эхо-клиент, использующий метод [`loop.create_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_connection), отправляет данные и ждёт, пока соединение не будет закрыто:498499```python500import asyncio501502class EchoClientProtocol(asyncio.Protocol):503    def __init__(self, message, on_con_lost):504        self.message = message505        self.on_con_lost = on_con_lost506507    def connection_made(self, transport):508        transport.write(self.message.encode())509        print('Data sent: {!r}'.format(self.message))510511    def data_received(self, data):512        print('Data received: {!r}'.format(data.decode()))513514    def connection_lost(self, exc):515        print('The server closed the connection')516        self.on_con_lost.set_result(True)517518async def main():519    # Получаем ссылку на цикл событий, так как планируем использовать520    # низкоуровневые API.521    loop = asyncio.get_running_loop()522523    on_con_lost = loop.create_future()524    message = 'Hello World!'525526    transport, protocol = await loop.create_connection(527        lambda: EchoClientProtocol(message, on_con_lost),528        '127.0.0.1', 8888)529530    # Ожидаем, пока протокол не сообщит, что соединение531    # потеряно, и закрываем транспорт.532    try:533        await on_con_lost534    finally:535        transport.close()536537asyncio.run(main())538```539540> **См. также**541>542> Пример [TCP-эхо-клиента с использованием потоков](https://python-all.ru/3.7/library/asyncio-stream.html#asyncio-tcp-echo-client-streams) использует высокоуровневую функцию [`asyncio.open_connection()`](https://python-all.ru/3.7/library/asyncio-stream.html#asyncio.open_connection).543544### UDP-эхо-сервер545546UDP-эхо-сервер, использующий метод [`loop.create_datagram_endpoint()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint), отправляет обратно полученные данные:547548```python549import asyncio550551class EchoServerProtocol:552    def connection_made(self, transport):553        self.transport = transport554555    def datagram_received(self, data, addr):556        message = data.decode()557        print('Received %r from %s' % (message, addr))558        print('Send %r to %s' % (message, addr))559        self.transport.sendto(data, addr)560561async def main():562    print("Starting UDP server")563564    # Получаем ссылку на цикл событий, так как планируем использовать565    # низкоуровневые API.566    loop = asyncio.get_running_loop()567568    # Будет создан один экземпляр протокола для обслуживания всех569    # запросов клиентов.570    transport, protocol = await loop.create_datagram_endpoint(571        lambda: EchoServerProtocol(),572        local_addr=('127.0.0.1', 9999))573574    try:575        await asyncio.sleep(3600)  # Обслуживать в течение 1 часа.576    finally:577        transport.close()578579asyncio.run(main())580```581582### UDP-эхо-клиент583584UDP-эхо-клиент с помощью метода [`loop.create_datagram_endpoint()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint) отправляет данные и закрывает транспорт при получении ответа:585586```python587import asyncio588589class EchoClientProtocol:590    def __init__(self, message, on_con_lost):591        self.message = message592        self.on_con_lost = on_con_lost593        self.transport = None594595    def connection_made(self, transport):596        self.transport = transport597        print('Send:', self.message)598        self.transport.sendto(self.message.encode())599600    def datagram_received(self, data, addr):601        print("Received:", data.decode())602603        print("Close the socket")604        self.transport.close()605606    def error_received(self, exc):607        print('Error received:', exc)608609    def connection_lost(self, exc):610        print("Connection closed")611        self.on_con_lost.set_result(True)612613async def main():614    # Получаем ссылку на цикл событий, так как планируем использовать615    # низкоуровневые API.616    loop = asyncio.get_running_loop()617618    on_con_lost = loop.create_future()619    message = "Hello World!"620621    transport, protocol = await loop.create_datagram_endpoint(622        lambda: EchoClientProtocol(message, on_con_lost),623        remote_addr=('127.0.0.1', 9999))624625    try:626        await on_con_lost627    finally:628        transport.close()629630asyncio.run(main())631```632633### Подключение существующих сокетов634635Ожидание получения данных сокетом с помощью метода [`loop.create_connection()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.create_connection) и протокола:636637```python638import asyncio639import socket640641class MyProtocol(asyncio.Protocol):642643    def __init__(self, on_con_lost):644        self.transport = None645        self.on_con_lost = on_con_lost646647    def connection_made(self, transport):648        self.transport = transport649650    def data_received(self, data):651        print("Received:", data.decode())652653        # Мы закончили: закрываем транспорт;654        # connection_lost() будет вызван автоматически.655        self.transport.close()656657    def connection_lost(self, exc):658        # Сокет был закрыт659        self.on_con_lost.set_result(True)660661async def main():662    # Получаем ссылку на цикл событий, так как планируем использовать663    # низкоуровневые API.664    loop = asyncio.get_running_loop()665    on_con_lost = loop.create_future()666667    # Создаём пару соединённых сокетов668    rsock, wsock = socket.socketpair()669670    # Регистрируем сокет для ожидания данных.671    transport, protocol = await loop.create_connection(672        lambda: MyProtocol(on_con_lost), sock=rsock)673674    # Имитируем приём данных из сети.675    loop.call_soon(wsock.send, 'abc'.encode())676677    try:678        await protocol.on_con_lost679    finally:680        transport.close()681        wsock.close()682683asyncio.run(main())684```685686> **См. также**687>688> В примере [наблюдения за файловым дескриптором на предмет событий чтения](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio-example-watch-fd) используется низкоуровневый метод [`loop.add_reader()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.add_reader) для регистрации FD.689>690> В примере [регистрации открытого сокета для ожидания данных с помощью потоков](https://python-all.ru/3.7/library/asyncio-stream.html#asyncio-example-create-connection-streams) используются высокоуровневые потоки , созданные функцией [`open_connection()`](https://python-all.ru/3.7/library/asyncio-stream.html#asyncio.open_connection) в корутине.691692### loop.subprocess\_exec() и SubprocessProtocol693694Пример протокола подпроцесса, используемого для получения вывода подпроцесса и ожидания его завершения.695696Подпроцесс создаётся методом [`loop.subprocess_exec()`](https://python-all.ru/3.7/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec):697698```python699import asyncio700import sys701702class DateProtocol(asyncio.SubprocessProtocol):703    def __init__(self, exit_future):704        self.exit_future = exit_future705        self.output = bytearray()706707    def pipe_data_received(self, fd, data):708        self.output.extend(data)709710    def process_exited(self):711        self.exit_future.set_result(True)712713async def get_date():714    # Получаем ссылку на цикл событий, так как планируем использовать715    # низкоуровневые API.716    loop = asyncio.get_running_loop()717718    code = 'import datetime; print(datetime.datetime.now())'719    exit_future = asyncio.Future(loop=loop)720721    # Создаём подпроцесс, управляемый DateProtocol;722    # перенаправляем стандартный вывод в канал.723    transport, protocol = await loop.subprocess_exec(724        lambda: DateProtocol(exit_future),725        sys.executable, '-c', code,726        stdin=None, stderr=None)727728    # Ожидаем завершения подпроцесса с помощью метода process_exited()729    # протокола.730    await exit_future731732    # Закрываем канал stdout.733    transport.close()734735    # Читаем вывод, который был собран методом736    # pipe_data_received() протокола.737    data = bytes(protocol.output)738    return data.decode('ascii').rstrip()739740if sys.platform == "win32":741    asyncio.set_event_loop_policy(742        asyncio.WindowsProactorEventLoopPolicy())743744date = asyncio.run(get_date())745print(f"Current date: {date}")746```747748См. также [тот же пример](https://python-all.ru/3.7/library/asyncio-subprocess.html#asyncio-example-create-subprocess-exec) , написанный с использованием высокоуровневых API.749