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

asyncio-protocol.md

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

1> **Источник:** https://python-all.ru/3.6/library/asyncio-protocol.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 18.5.4. Транспорты и протоколы (API на основе колбэков)89**Исходный код:** [Lib/asyncio/transports.py](https://python-all.ru/src/3.6/Lib/asyncio/transports.py)1011**Исходный код:** [Lib/asyncio/protocols.py](https://python-all.ru/src/3.6/Lib/asyncio/protocols.py)1213## 18.5.4.1. Транспорты1415Транспорты – это классы, предоставляемые [`asyncio`](https://python-all.ru/3.6/library/asyncio.html#module-asyncio) для абстракции различных видов каналов связи. Обычно транспорт не создаётся вручную; вместо этого вызывается метод [`AbstractEventLoop`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop), который создаёт транспорт и пытается инициировать базовый канал связи, вызывая колбэк при успехе.1617После установки канала связи транспорт всегда связан с экземпляром [протокола](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio-protocol). Затем протокол может вызывать методы транспорта для различных целей.1819[`asyncio`](https://python-all.ru/3.6/library/asyncio.html#module-asyncio) в настоящее время реализует транспорты для TCP, UDP, SSL и каналов подпроцессов. Доступные методы транспорта зависят от его типа.2021Классы транспортов [не потокобезопасны](https://python-all.ru/3.6/library/asyncio-dev.html#asyncio-multithreading).2223Изменено в версии 3.6: Параметр сокета `TCP_NODELAY` теперь устанавливается по умолчанию.2425### 18.5.4.1.1. BaseTransport2627#### `class asyncio.BaseTransport`2829Базовый класс для транспортов.3031#### `close()`3233Закрывает транспорт. Если у транспорта есть буфер для исходящих данных, буферизированные данные будут асинхронно сброшены. Новые данные приниматься не будут. После сброса всех буферизированных данных будет вызван метод `connection_lost()` протокола с аргументом [`None`](https://python-all.ru/3.6/library/constants.html#None).3435#### `is_closing()`3637Возвращает `True`, если транспорт закрывается или уже закрыт.3839Новое в версии 3.5.1.4041#### `get_extra_info(name, default=None)`4243Возвращает дополнительную информацию о транспорте. *name* – это строка, представляющая часть информации, специфичной для транспорта, которую требуется получить; *default* – значение, возвращаемое, если информация отсутствует.4445Этот метод позволяет реализациям транспорта легко предоставлять информацию, специфичную для канала.4647- сокет:4849  - `'peername'`: удаленный адрес, к которому подключен сокет, результат [`socket.socket.getpeername()`](https://python-all.ru/3.6/library/socket.html#socket.socket.getpeername) (`None` при ошибке)50  - `'socket'`: экземпляр [`socket.socket`](https://python-all.ru/3.6/library/socket.html#socket.socket)51  - `'sockname'`: собственный адрес сокета, результат [`socket.socket.getsockname()`](https://python-all.ru/3.6/library/socket.html#socket.socket.getsockname)52- SSL-сокет:5354  - `'compression'`: алгоритм сжатия, используемый в виде строки, или `None`, если соединение не сжато; результат [`ssl.SSLSocket.compression()`](https://python-all.ru/3.6/library/ssl.html#ssl.SSLSocket.compression)55  - `'cipher'`: кортеж из трех значений, содержащий название используемого шифра, версию протокола SSL, определяющую его использование, и количество используемых секретных битов; результат [`ssl.SSLSocket.cipher()`](https://python-all.ru/3.6/library/ssl.html#ssl.SSLSocket.cipher)56  - `'peercert'`: сертификат однорангового узла; результат [`ssl.SSLSocket.getpeercert()`](https://python-all.ru/3.6/library/ssl.html#ssl.SSLSocket.getpeercert)57  - `'sslcontext'`: экземпляр [`ssl.SSLContext`](https://python-all.ru/3.6/library/ssl.html#ssl.SSLContext)58  - `'ssl_object'`: экземпляр [`ssl.SSLObject`](https://python-all.ru/3.6/library/ssl.html#ssl.SSLObject) или [`ssl.SSLSocket`](https://python-all.ru/3.6/library/ssl.html#ssl.SSLSocket)59- канал:6061  - `'pipe'`: объект канала62- подпроцесс:6364  - `'subprocess'`: экземпляр [`subprocess.Popen`](https://python-all.ru/3.6/library/subprocess.html#subprocess.Popen)6566#### `set_protocol(protocol)`6768Устанавливает новый протокол. Смену протокола следует выполнять только в случае, если оба протокола документально поддерживают переключение.6970Новое в версии 3.5.3.7172#### `get_protocol()`7374Возвращает текущий протокол.7576Новое в версии 3.5.3.7778Изменено в версии 3.5.1: информация `'ssl_object'` добавлена к SSL-сокетам.7980### 18.5.4.1.2. ReadTransport8182#### `class asyncio.ReadTransport`8384Интерфейс для транспортов, доступных только для чтения.8586#### `pause_reading()`8788Приостанавливает приём данных на транспорте. Данные не будут передаваться методу `data_received()` протокола, пока не будет вызван [`resume_reading()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.ReadTransport.resume_reading).8990Изменено в версии 3.6.7: Метод является идемпотентным, то есть его можно вызывать, когда транспорт уже приостановлен или закрыт.9192#### `resume_reading()`9394Возобновляет приём данных. Метод `data_received()` протокола будет вызван снова, если появились данные для чтения.9596Изменено в версии 3.6.7: Метод является идемпотентным, то есть его можно вызывать, когда транспорт уже читает.9798### 18.5.4.1.3. WriteTransport99100#### `class asyncio.WriteTransport`101102Интерфейс для транспортов, доступных только для записи.103104#### `abort()`105106Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод `connection_lost()` протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.6/library/constants.html#None).107108#### `can_write_eof()`109110Возвращает [`True`](https://python-all.ru/3.6/library/constants.html#True), если транспорт поддерживает [`write_eof()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), и [`False`](https://python-all.ru/3.6/library/constants.html#False) в противном случае.111112#### `get_write_buffer_size()`113114Возвращает текущий размер выходного буфера, используемого транспортом.115116#### `get_write_buffer_limits()`117118Возвращает пределы *high* и *low* для управления потоком записи. Возвращает кортеж `(low, high)`, где *low* и *high* – положительные числа байтов.119120Используйте [`set_write_buffer_limits()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) для установки границ.121122Новое в версии 3.4.2.123124#### `set_write_buffer_limits(high=None, low=None)`125126Устанавливает *high*- и *low*-пределы для управления потоком записи.127128Эти два значения (измеряются в байтах) определяют, когда вызываются методы `pause_writing()` и `resume_writing()` протокола. Если заданы, нижний предел должен быть меньше или равен верхнему. Ни *high*, ни *low* не могут быть отрицательными.129130`pause_writing()` вызывается, когда размер буфера становится больше или равен значению *high*. Если запись была приостановлена, `resume_writing()` вызывается, когда размер буфера становится меньше или равен значению *low*.131132Значения по умолчанию зависят от реализации. Если задан только верхний предел, нижний предел по умолчанию равен значению, зависящему от реализации, которое меньше или равно верхнему пределу. Установка *high* в ноль принудительно устанавливает *low* в ноль и приводит к вызову `pause_writing()` всякий раз, когда буфер становится непустым. Установка *low* в ноль приводит к вызову `resume_writing()` только когда буфер становится пустым. Использование нуля для любого из пределов в целом неоптимально, так как снижает возможности для одновременного выполнения ввода-вывода и вычислений.133134Используйте [`get_write_buffer_limits()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.WriteTransport.get_write_buffer_limits) для получения пределов.135136#### `write(data)`137138Записывает несколько байт *данных* в транспорт.139140Этот метод не блокирует выполнение; он буферизует данные и обеспечивает их асинхронную отправку.141142#### `writelines(list_of_data)`143144Записывает список (или любой итерабельный объект) байтов данных в транспорт. Это функционально эквивалентно вызову [`write()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.WriteTransport.write) для каждого элемента, возвращаемого итератором, но может быть реализовано более эффективно.145146#### `write_eof()`147148Закрывает конец записи транспорта после сброса буферизованных данных. Данные всё ещё могут приниматься.149150Этот метод может вызывать [`NotImplementedError`](https://python-all.ru/3.6/library/exceptions.html#NotImplementedError), если транспорт (например, SSL) не поддерживает полузакрытие.151152### 18.5.4.1.4. DatagramTransport153154#### `DatagramTransport.sendto(data, addr=None)`155156Отправляет байты *data* удалённому узлу, заданному *addr* (зависящий от транспорта целевой адрес). Если *addr* равно [`None`](https://python-all.ru/3.6/library/constants.html#None), данные отправляются на целевой адрес, заданный при создании транспорта.157158Этот метод не блокирует выполнение; он буферизует данные и обеспечивает их асинхронную отправку.159160#### `DatagramTransport.abort()`161162Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод `connection_lost()` протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.6/library/constants.html#None).163164### 18.5.4.1.5. BaseSubprocessTransport165166#### `class asyncio.BaseSubprocessTransport`167168#### `get_pid()`169170Возвращает идентификатор процесса подпроцесса в виде целого числа.171172#### `get_pipe_transport(fd)`173174Возвращает транспорт для канала связи, соответствующего целочисленному файловому дескриптору *fd*:175176- `0`: читаемый потоковый транспорт стандартного ввода (*stdin*) или [`None`](https://python-all.ru/3.6/library/constants.html#None), если подпроцесс был создан без `stdin=PIPE`177- `1`: записываемый потоковый транспорт стандартного вывода (*stdout*) или [`None`](https://python-all.ru/3.6/library/constants.html#None), если подпроцесс был создан без `stdout=PIPE`178- `2`: записываемый потоковый транспорт стандартного вывода ошибок (*stderr*) или [`None`](https://python-all.ru/3.6/library/constants.html#None), если подпроцесс был создан без `stderr=PIPE`179- другой *fd*: [`None`](https://python-all.ru/3.6/library/constants.html#None)180181#### `get_returncode()`182183Возвращает код возврата подпроцесса в виде целого числа или [`None`](https://python-all.ru/3.6/library/constants.html#None), если он ещё не завершился, аналогично атрибуту [`subprocess.Popen.returncode`](https://python-all.ru/3.6/library/subprocess.html#subprocess.Popen.returncode).184185#### `kill()`186187Завершает подпроцесс, как в [`subprocess.Popen.kill()`](https://python-all.ru/3.6/library/subprocess.html#subprocess.Popen.kill).188189В системах POSIX функция отправляет подпроцессу сигнал SIGKILL. В Windows этот метод является псевдонимом для [`terminate()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport.terminate).190191#### `send_signal(signal)`192193Отправляет подпроцессу номер *signal*, как в [`subprocess.Popen.send_signal()`](https://python-all.ru/3.6/library/subprocess.html#subprocess.Popen.send_signal).194195#### `terminate()`196197Запрашивает остановку подпроцесса, как в [`subprocess.Popen.terminate()`](https://python-all.ru/3.6/library/subprocess.html#subprocess.Popen.terminate). Этот метод является псевдонимом метода [`close()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport.close).198199На системах POSIX этот метод отправляет сигнал SIGTERM подпроцессу. В Windows вызывается функция Windows API TerminateProcess() для остановки подпроцесса.200201#### `close()`202203Запрашивает остановку подпроцесса вызовом метода [`terminate()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport.terminate), если подпроцесс ещё не завершился, и закрывает транспорты всех каналов (*stdin*, *stdout* и *stderr*).204205## 18.5.4.2. Protocols206207[`asyncio`](https://python-all.ru/3.6/library/asyncio.html#module-asyncio) предоставляет базовые классы, которые можно наследовать для реализации своих сетевых протоколов. Эти классы используются вместе с [транспортами](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio-transport) (см. ниже): протокол разбирает входящие данные и запрашивает запись исходящих данных, а транспорт отвечает за фактический ввод-вывод и буферизацию.208209При наследовании класса протокола рекомендуется переопределять определённые методы. Эти методы являются колбэками: они будут вызваны транспортом при определённых событиях (например, при получении некоторых данных); не следует вызывать их самостоятельно, если только вы не реализуете транспорт.210211> **Примечание**212>213> Все колбэки имеют реализации по умолчанию, которые являются пустыми. Поэтому нужно реализовывать только те колбэки, которые соответствуют интересующим событиям.214215### 18.5.4.2.1. Классы протоколов216217#### `class asyncio.Protocol`218219Базовый класс для реализации потоковых протоколов (для использования, например, с транспортами TCP и SSL).220221#### `class asyncio.DatagramProtocol`222223Базовый класс для реализации дейтаграммных протоколов (для использования, например, с транспортами UDP).224225#### `class asyncio.SubprocessProtocol`226227Базовый класс для реализации протоколов, взаимодействующих с дочерними процессами (через набор однонаправленных каналов).228229### 18.5.4.2.2. Connection callbacks230231Эти колбэки могут вызываться для экземпляров [`Protocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.Protocol), [`DatagramProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.SubprocessProtocol):232233#### `BaseProtocol.connection_made(transport)`234235Вызывается при установлении соединения.236237Аргумент *транспорт* представляет соединение. Если он понадобится, его следует сохранить (например, как атрибут).238239#### `BaseProtocol.connection_lost(exc)`240241Вызывается при потере или закрытии соединения.242243The argument is either an exception object or [`None`](https://python-all.ru/3.6/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.244245[`connection_made()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_made) и [`connection_lost()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) вызываются ровно один раз за каждое успешное соединение. Все остальные колбэки вызываются между этими двумя методами, что упрощает управление ресурсами в реализации протокола.246247Следующие колбэки могут вызываться только для экземпляров [`SubprocessProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.SubprocessProtocol):248249#### `SubprocessProtocol.pipe_data_received(fd, data)`250251Вызывается, когда дочерний процесс записывает данные в свой канал stdout или stderr. *fd* – это целочисленный файловый дескриптор канала. *data* – это непустой объект bytes, содержащий данные.252253#### `SubprocessProtocol.pipe_connection_lost(fd, exc)`254255Вызывается, когда один из каналов, связывающих с дочерним процессом, закрывается. *fd* – это целочисленный файловый дескриптор закрытого канала.256257#### `SubprocessProtocol.process_exited()`258259Вызывается, когда дочерний процесс завершился.260261### 18.5.4.2.3. Потоковые протоколы262263Следующие колбэки вызываются для экземпляров [`Protocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.Protocol):264265#### `Protocol.data_received(data)`266267Вызывается при получении данных. *data* – это непустой объект bytes, содержащий входящие данные.268269> **Примечание**270>271> Буферизация, фрагментация или сборка данных зависит от транспорта. В общем случае не следует полагаться на конкретную семантику, а делать разбор достаточно общим и гибким. Однако данные всегда принимаются в правильном порядке.272273#### `Protocol.eof_received()`274275Вызывается, когда удалённая сторона сигнализирует, что больше не будет отправлять данные (например, вызовом `write_eof()`, если другая сторона также использует asyncio).276277Этот метод может вернуть ложное значение (включая `None`), и в этом случае транспорт закроется сам. Если же метод возвращает истинное значение, закрытие транспорта остаётся на усмотрение протокола. Поскольку реализация по умолчанию возвращает `None`, соединение неявно закрывается.278279> **Примечание**280>281> Некоторые транспорты, такие как SSL, не поддерживают полузакрытые соединения; в этом случае возврат истинного значения из метода не предотвращает закрытие соединения.282283`data_received()` может вызываться произвольное количество раз в течение соединения. Однако `eof_received()` вызывается не более одного раза, и после его вызова `data_received()` уже не будет вызван.284285Автомат состояний:286287> start -\> [`connection_made()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_made) \[-\> [`data_received()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.Protocol.data_received) \*\] \[-\> [`eof_received()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.Protocol.eof_received) ?\] -\> [`connection_lost()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) -\> end288289### 18.5.4.2.4. Дейтаграммные протоколы290291Следующие колбэки вызываются для экземпляров [`DatagramProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.DatagramProtocol).292293#### `DatagramProtocol.datagram_received(data, addr)`294295Вызывается при получении дейтаграммы. *data* – это байтовый объект, содержащий входящие данные. *addr* – адрес узла, отправляющего данные; точный формат зависит от транспорта.296297#### `DatagramProtocol.error_received(exc)`298299Вызывается, когда предыдущая операция отправки или получения вызывает исключение [`OSError`](https://python-all.ru/3.6/library/exceptions.html#OSError). *exc* – экземпляр [`OSError`](https://python-all.ru/3.6/library/exceptions.html#OSError).300301Этот метод вызывается в редких случаях, когда транспорт (например, UDP) обнаруживает, что дейтаграмма не может быть доставлена получателю. Однако во многих случаях недоставленные дейтаграммы просто молча отбрасываются.302303### 18.5.4.2.5. Колбэки управления потоком304305Эти колбэки могут вызываться для экземпляров [`Protocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.Protocol), [`DatagramProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.SubprocessProtocol):306307#### `BaseProtocol.pause_writing()`308309Вызывается, когда буфер транспорта превышает верхнюю границу (high-water mark).310311#### `BaseProtocol.resume_writing()`312313Вызывается, когда буфер транспорта опускается ниже нижней границы (low-water mark).314315Вызовы `pause_writing()` и `resume_writing()` парные – `pause_writing()` вызывается один раз, когда буфер строго превышает верхнюю границу (даже если последующие записи ещё больше увеличивают размер буфера), и затем `resume_writing()` вызывается один раз, когда размер буфера достигает нижней границы.316317> **Примечание**318>319> Если размер буфера равен верхней границе, `pause_writing()` не вызывается – он должен быть строго выше. И наоборот, `resume_writing()` вызывается, когда размер буфера меньше или равен нижней границе. Эти граничные условия важны, чтобы всё работало ожидаемым образом, когда одна из границ равна нулю.320321> **Примечание**322>323> В системах BSD (OS X, FreeBSD и т.д.) управление потоком не поддерживается для [`DatagramProtocol`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.DatagramProtocol), так как ошибки отправки из-за записи слишком большого количества пакетов трудно обнаружить. Сокет всегда кажется «готовым», и лишние пакеты отбрасываются; исключение [`OSError`](https://python-all.ru/3.6/library/exceptions.html#OSError) с errno, установленным в [`errno.ENOBUFS`](https://python-all.ru/3.6/library/errno.html#errno.ENOBUFS), может быть возбуждено или нет; если оно возбуждено, оно будет сообщено в [`DatagramProtocol.error_received()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.DatagramProtocol.error_received), но в остальном игнорируется.324325### 18.5.4.2.6. Корутины и протоколы326327Корутины могут быть запланированы в методе протокола с помощью [`ensure_future()`](https://python-all.ru/3.6/library/asyncio-task.html#asyncio.ensure_future), но порядок выполнения не гарантируется. Протоколы не знают о корутинах, созданных в их методах, и поэтому не будут ждать их.328329Для надёжного порядка выполнения используйте [потоковые объекты](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio-streams) в корутине с `yield from`. Например, корутину [`StreamWriter.drain()`](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio.StreamWriter.drain) можно использовать для ожидания, пока буфер записи не будет сброшен.330331## 18.5.4.3. Примеры протоколов332333### 18.5.4.3.1. TCP-клиент протокола эхо334335TCP-клиент эхо, использующий метод [`AbstractEventLoop.create_connection()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_connection), отправляет данные и ждёт, пока соединение не будет закрыто:336337```python338import asyncio339340class EchoClientProtocol(asyncio.Protocol):341    def __init__(self, message, loop):342        self.message = message343        self.loop = loop344345    def connection_made(self, transport):346        transport.write(self.message.encode())347        print('Data sent: {!r}'.format(self.message))348349    def data_received(self, data):350        print('Data received: {!r}'.format(data.decode()))351352    def connection_lost(self, exc):353        print('The server closed the connection')354        print('Stop the event loop')355        self.loop.stop()356357loop = asyncio.get_event_loop()358message = 'Hello World!'359coro = loop.create_connection(lambda: EchoClientProtocol(message, loop),360                              '127.0.0.1', 8888)361loop.run_until_complete(coro)362loop.run_forever()363loop.close()364```365366Цикл событий запускается дважды. Метод [`run_until_complete()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.run_until_complete) предпочтительнее в этом коротком примере, чтобы вызвать исключение, если сервер не прослушивается, вместо того чтобы писать короткую корутину для обработки исключения и остановки работающего цикла. При выходе [`run_until_complete()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.run_until_complete) цикл больше не работает, поэтому нет необходимости останавливать цикл в случае ошибки.367368> **См. также**369>370> Пример [TCP-эхо-клиента с использованием потоков данных](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio-tcp-echo-client-streams) использует функцию [`asyncio.open_connection()`](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio.open_connection).371372### 18.5.4.3.2. Протокол TCP-эхо-сервера373374TCP-эхо-сервер с использованием метода [`AbstractEventLoop.create_server()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_server), отправляет обратно полученные данные и закрывает соединение:375376```python377import asyncio378379class EchoServerClientProtocol(asyncio.Protocol):380    def connection_made(self, transport):381        peername = transport.get_extra_info('peername')382        print('Connection from {}'.format(peername))383        self.transport = transport384385    def data_received(self, data):386        message = data.decode()387        print('Data received: {!r}'.format(message))388389        print('Send: {!r}'.format(message))390        self.transport.write(data)391392        print('Close the client socket')393        self.transport.close()394395loop = asyncio.get_event_loop()396# Каждое клиентское соединение создаёт новый экземпляр протокола.397coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)398server = loop.run_until_complete(coro)399400# Обслуживать запросы до нажатия Ctrl+C.401print('Serving on {}'.format(server.sockets[0].getsockname()))402try:403    loop.run_forever()404except KeyboardInterrupt:405    pass406407# Закрыть сервер.408server.close()409loop.run_until_complete(server.wait_closed())410loop.close()411```412413`Transport.close()` можно вызвать сразу после [`WriteTransport.write()`](https://python-all.ru/3.6/library/asyncio-protocol.html#asyncio.WriteTransport.write), даже если данные еще не отправлены на сокет: оба метода асинхронны. `yield from` не нужен, поскольку эти методы транспорта не являются корутинами.414415> **См. также**416>417> Пример [TCP-эхо-сервера с использованием потоков данных](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio-tcp-echo-server-streams) использует функцию [`asyncio.start_server()`](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio.start_server).418419### 18.5.4.3.3. Протокол UDP-эхо-клиента420421UDP-эхо-клиент с использованием метода [`AbstractEventLoop.create_datagram_endpoint()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_datagram_endpoint) отправляет данные и закрывает транспорт при получении ответа:422423```python424import asyncio425426class EchoClientProtocol:427    def __init__(self, message, loop):428        self.message = message429        self.loop = loop430        self.transport = None431432    def connection_made(self, transport):433        self.transport = transport434        print('Send:', self.message)435        self.transport.sendto(self.message.encode())436437    def datagram_received(self, data, addr):438        print("Received:", data.decode())439440        print("Close the socket")441        self.transport.close()442443    def error_received(self, exc):444        print('Error received:', exc)445446    def connection_lost(self, exc):447        print("Socket closed, stop the event loop")448        loop = asyncio.get_event_loop()449        loop.stop()450451loop = asyncio.get_event_loop()452message = "Hello World!"453connect = loop.create_datagram_endpoint(454    lambda: EchoClientProtocol(message, loop),455    remote_addr=('127.0.0.1', 9999))456transport, protocol = loop.run_until_complete(connect)457loop.run_forever()458transport.close()459loop.close()460```461462### 18.5.4.3.4. Протокол UDP-эхо-сервера463464UDP-эхо-сервер с использованием метода [`AbstractEventLoop.create_datagram_endpoint()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_datagram_endpoint) отправляет обратно полученные данные:465466```python467import asyncio468469class EchoServerProtocol:470    def connection_made(self, transport):471        self.transport = transport472473    def datagram_received(self, data, addr):474        message = data.decode()475        print('Received %r from %s' % (message, addr))476        print('Send %r to %s' % (message, addr))477        self.transport.sendto(data, addr)478479loop = asyncio.get_event_loop()480print("Starting UDP server")481# Будет создан один экземпляр протокола для обслуживания всех клиентских запросов.482listen = loop.create_datagram_endpoint(483    EchoServerProtocol, local_addr=('127.0.0.1', 9999))484transport, protocol = loop.run_until_complete(listen)485486try:487    loop.run_forever()488except KeyboardInterrupt:489    pass490491transport.close()492loop.close()493```494495### 18.5.4.3.5. Регистрация открытого сокета для ожидания данных с использованием протокола496497Ожидать, пока сокет получит данные, используя метод [`AbstractEventLoop.create_connection()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_connection) с протоколом, а затем закрыть цикл событий498499```python500import asyncio501try:502    from socket import socketpair503except ImportError:504    from asyncio.windows_utils import socketpair505506# Создаём пару соединённых сокетов507rsock, wsock = socketpair()508loop = asyncio.get_event_loop()509510class MyProtocol(asyncio.Protocol):511    transport = None512513    def connection_made(self, transport):514        self.transport = transport515516    def data_received(self, data):517        print("Received:", data.decode())518519        # Готово: закрыть транспорт (он вызовет connection_lost()).520        self.transport.close()521522    def connection_lost(self, exc):523        # Сокет закрыт, остановить цикл событий.524        loop.stop()525526# Зарегистрировать сокет для ожидания данных.527connect_coro = loop.create_connection(MyProtocol, sock=rsock)528transport, protocol = loop.run_until_complete(connect_coro)529530# Симулировать приём данных из сети531loop.call_soon(wsock.send, 'abc'.encode())532533# Запустить цикл событий534loop.run_forever()535536# Готово, закрыть сокеты и цикл событий.537rsock.close()538wsock.close()539loop.close()540```541542> **См. также**543>544> Пример [наблюдения за файловым дескриптором на предмет событий чтения](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio-watch-read-event) использует низкоуровневый [`AbstractEventLoop.add_reader()`](https://python-all.ru/3.6/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.add_reader) метод для регистрации файлового дескриптора сокета.545>546> В примере [регистрации открытого сокета для ожидания данных с помощью потоков](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio-register-socket-streams) используются высокоуровневые потоки , созданные функцией [`open_connection()`](https://python-all.ru/3.6/library/asyncio-stream.html#asyncio.open_connection) в корутине.547