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

asyncio-protocol.md

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

1> **Источник:** https://python-all.ru/3.5/library/asyncio-protocol.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 18.5.4. Транспорты и протоколы (API на основе колбэков)89## 18.5.4.1. Транспорты1011Транспорты – это классы, предоставляемые [`asyncio`](https://python-all.ru/3.5/library/asyncio.html#module-asyncio) для абстракции различных видов каналов связи. Обычно транспорт не создаётся вручную; вместо этого вызывается метод [`AbstractEventLoop`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop), который создаёт транспорт и пытается инициировать базовый канал связи, вызывая колбэк при успехе.1213После установки канала связи транспорт всегда связан с экземпляром [протокола](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio-protocol). Затем протокол может вызывать методы транспорта для различных целей.1415[`asyncio`](https://python-all.ru/3.5/library/asyncio.html#module-asyncio) в настоящее время реализует транспорты для TCP, UDP, SSL и каналов подпроцессов. Доступные методы транспорта зависят от его типа.1617Классы транспортов [не потокобезопасны](https://python-all.ru/3.5/library/asyncio-dev.html#asyncio-multithreading).1819### 18.5.4.1.1. BaseTransport2021#### `class asyncio.BaseTransport`2223Базовый класс для транспортов.2425#### `close()`2627Закрывает транспорт. Если у транспорта есть буфер для исходящих данных, буферизированные данные будут асинхронно сброшены. Новые данные приниматься не будут. После сброса всех буферизированных данных будет вызван метод `connection_lost()` протокола с аргументом [`None`](https://python-all.ru/3.5/library/constants.html#None).2829#### `is_closing()`3031Возвращает `True`, если транспорт закрывается или уже закрыт.3233Новое в версии 3.5.1.3435#### `get_extra_info(name, default=None)`3637Возвращает дополнительную информацию о транспорте. *name* – это строка, представляющая часть информации, специфичной для транспорта, которую требуется получить; *default* – значение, возвращаемое, если информация отсутствует.3839Этот метод позволяет реализациям транспорта легко предоставлять информацию, специфичную для канала.4041- сокет:4243  - `'peername'`: удаленный адрес, к которому подключен сокет, результат [`socket.socket.getpeername()`](https://python-all.ru/3.5/library/socket.html#socket.socket.getpeername) (`None` при ошибке)44  - `'socket'`: экземпляр [`socket.socket`](https://python-all.ru/3.5/library/socket.html#socket.socket)45  - `'sockname'`: собственный адрес сокета, результат [`socket.socket.getsockname()`](https://python-all.ru/3.5/library/socket.html#socket.socket.getsockname)46- SSL-сокет:4748  - `'compression'`: алгоритм сжатия, используемый в виде строки, или `None`, если соединение не сжато; результат [`ssl.SSLSocket.compression()`](https://python-all.ru/3.5/library/ssl.html#ssl.SSLSocket.compression)49  - `'cipher'`: кортеж из трех значений, содержащий название используемого шифра, версию протокола SSL, определяющую его использование, и количество используемых секретных битов; результат [`ssl.SSLSocket.cipher()`](https://python-all.ru/3.5/library/ssl.html#ssl.SSLSocket.cipher)50  - `'peercert'`: сертификат однорангового узла; результат [`ssl.SSLSocket.getpeercert()`](https://python-all.ru/3.5/library/ssl.html#ssl.SSLSocket.getpeercert)51  - `'sslcontext'`: экземпляр [`ssl.SSLContext`](https://python-all.ru/3.5/library/ssl.html#ssl.SSLContext)52  - `'ssl_object'`: экземпляр [`ssl.SSLObject`](https://python-all.ru/3.5/library/ssl.html#ssl.SSLObject) или [`ssl.SSLSocket`](https://python-all.ru/3.5/library/ssl.html#ssl.SSLSocket)53- канал:5455  - `'pipe'`: объект канала56- подпроцесс:5758  - `'subprocess'`: экземпляр [`subprocess.Popen`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen)5960#### `set_protocol(protocol)`6162Устанавливает новый протокол. Смену протокола следует выполнять только в случае, если оба протокола документально поддерживают переключение.6364Новое в версии 3.5.3.6566#### `get_protocol()`6768Возвращает текущий протокол.6970Новое в версии 3.5.3.7172Изменено в версии 3.5.1: информация `'ssl_object'` добавлена к SSL-сокетам.7374### 18.5.4.1.2. ReadTransport7576#### `class asyncio.ReadTransport`7778Интерфейс для транспортов, доступных только для чтения.7980#### `pause_reading()`8182Приостанавливает приём данных на транспорте. Данные не будут передаваться методу `data_received()` протокола, пока не будет вызван [`resume_reading()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.ReadTransport.resume_reading).8384#### `resume_reading()`8586Возобновляет приём данных. Метод `data_received()` протокола будет вызван снова, если появились данные для чтения.8788### 18.5.4.1.3. WriteTransport8990#### `class asyncio.WriteTransport`9192Интерфейс для транспортов, доступных только для записи.9394#### `abort()`9596Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод `connection_lost()` протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.5/library/constants.html#None).9798#### `can_write_eof()`99100Возвращает [`True`](https://python-all.ru/3.5/library/constants.html#True), если транспорт поддерживает [`write_eof()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.WriteTransport.write_eof), и [`False`](https://python-all.ru/3.5/library/constants.html#False) в противном случае.101102#### `get_write_buffer_size()`103104Возвращает текущий размер выходного буфера, используемого транспортом.105106#### `get_write_buffer_limits()`107108Возвращает пределы *high* и *low* для управления потоком записи. Возвращает кортеж `(low, high)`, где *low* и *high* – положительные числа байтов.109110Используйте [`set_write_buffer_limits()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.WriteTransport.set_write_buffer_limits) для установки границ.111112Новое в версии 3.4.2.113114#### `set_write_buffer_limits(high=None, low=None)`115116Устанавливает *high*- и *low*-пределы для управления потоком записи.117118Эти два значения (измеряются в байтах) определяют, когда вызываются методы `pause_writing()` и `resume_writing()` протокола. Если заданы, нижний предел должен быть меньше или равен верхнему. Ни *high*, ни *low* не могут быть отрицательными.119120`pause_writing()` вызывается, когда размер буфера становится больше или равен значению *high*. Если запись была приостановлена, `resume_writing()` вызывается, когда размер буфера становится меньше или равен значению *low*.121122Значения по умолчанию зависят от реализации. Если задан только верхний предел, нижний предел по умолчанию равен значению, зависящему от реализации, которое меньше или равно верхнему пределу. Установка *high* в ноль принудительно устанавливает *low* в ноль и приводит к вызову `pause_writing()` всякий раз, когда буфер становится непустым. Установка *low* в ноль приводит к вызову `resume_writing()` только когда буфер становится пустым. Использование нуля для любого из пределов в целом неоптимально, так как снижает возможности для одновременного выполнения ввода-вывода и вычислений.123124Используйте [`get_write_buffer_limits()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.WriteTransport.get_write_buffer_limits) для получения пределов.125126#### `write(data)`127128Записывает несколько байт *данных* в транспорт.129130Этот метод не блокирует выполнение; он буферизует данные и обеспечивает их асинхронную отправку.131132#### `writelines(list_of_data)`133134Записывает список (или любой итерабельный объект) байтов данных в транспорт. Это функционально эквивалентно вызову [`write()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.WriteTransport.write) для каждого элемента, возвращаемого итератором, но может быть реализовано более эффективно.135136#### `write_eof()`137138Закрывает конец записи транспорта после сброса буферизованных данных. Данные всё ещё могут приниматься.139140Этот метод может вызывать [`NotImplementedError`](https://python-all.ru/3.5/library/exceptions.html#NotImplementedError), если транспорт (например, SSL) не поддерживает полузакрытие.141142### 18.5.4.1.4. DatagramTransport143144#### `DatagramTransport.sendto(data, addr=None)`145146Отправляет байты *data* удалённому узлу, заданному *addr* (зависящий от транспорта целевой адрес). Если *addr* равно [`None`](https://python-all.ru/3.5/library/constants.html#None), данные отправляются на целевой адрес, заданный при создании транспорта.147148Этот метод не блокирует выполнение; он буферизует данные и обеспечивает их асинхронную отправку.149150#### `DatagramTransport.abort()`151152Закрывает транспорт немедленно, не дожидаясь ожидающих операций до завершения. Буферизованные данные будут потеряны. Новые данные приниматься не будут. Метод `connection_lost()` протокола в конечном итоге будет вызван с аргументом [`None`](https://python-all.ru/3.5/library/constants.html#None).153154### 18.5.4.1.5. BaseSubprocessTransport155156#### `class asyncio.BaseSubprocessTransport`157158#### `get_pid()`159160Возвращает идентификатор процесса подпроцесса в виде целого числа.161162#### `get_pipe_transport(fd)`163164Возвращает транспорт для канала связи, соответствующего целочисленному файловому дескриптору *fd*:165166- `0`: читаемый потоковый транспорт стандартного ввода (*stdin*) или [`None`](https://python-all.ru/3.5/library/constants.html#None), если подпроцесс был создан без `stdin=PIPE`167- `1`: записываемый потоковый транспорт стандартного вывода (*stdout*) или [`None`](https://python-all.ru/3.5/library/constants.html#None), если подпроцесс был создан без `stdout=PIPE`168- `2`: записываемый потоковый транспорт стандартного вывода ошибок (*stderr*) или [`None`](https://python-all.ru/3.5/library/constants.html#None), если подпроцесс был создан без `stderr=PIPE`169- другой *fd*: [`None`](https://python-all.ru/3.5/library/constants.html#None)170171#### `get_returncode()`172173Возвращает код возврата подпроцесса в виде целого числа или [`None`](https://python-all.ru/3.5/library/constants.html#None), если он ещё не завершился, аналогично атрибуту [`subprocess.Popen.returncode`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.returncode).174175#### `kill()`176177Завершает подпроцесс, как в [`subprocess.Popen.kill()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.kill).178179В системах POSIX функция отправляет подпроцессу сигнал SIGKILL. В Windows этот метод является псевдонимом для [`terminate()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport.terminate).180181#### `send_signal(signal)`182183Отправляет подпроцессу номер *signal*, как в [`subprocess.Popen.send_signal()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.send_signal).184185#### `terminate()`186187Запрашивает остановку подпроцесса, как в [`subprocess.Popen.terminate()`](https://python-all.ru/3.5/library/subprocess.html#subprocess.Popen.terminate). Этот метод является псевдонимом метода [`close()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport.close).188189На системах POSIX этот метод отправляет сигнал SIGTERM подпроцессу. В Windows вызывается функция Windows API TerminateProcess() для остановки подпроцесса.190191#### `close()`192193Запрашивает остановку подпроцесса вызовом метода [`terminate()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseSubprocessTransport.terminate), если подпроцесс ещё не завершился, и закрывает транспорты всех каналов (*stdin*, *stdout* и *stderr*).194195## 18.5.4.2. Протоколы196197[`asyncio`](https://python-all.ru/3.5/library/asyncio.html#module-asyncio) предоставляет базовые классы, которые можно наследовать для реализации своих сетевых протоколов. Эти классы используются вместе с [транспортами](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio-transport) (см. ниже): протокол разбирает входящие данные и запрашивает запись исходящих данных, а транспорт отвечает за фактический ввод-вывод и буферизацию.198199При наследовании класса протокола рекомендуется переопределять определённые методы. Эти методы являются колбэками: они будут вызваны транспортом при определённых событиях (например, при получении некоторых данных); не следует вызывать их самостоятельно, если только вы не реализуете транспорт.200201> **Примечание**202>203> Все колбэки имеют реализации по умолчанию, которые являются пустыми. Поэтому нужно реализовывать только те колбэки, которые соответствуют интересующим событиям.204205### 18.5.4.2.1. Классы протоколов206207#### `class asyncio.Protocol`208209Базовый класс для реализации потоковых протоколов (для использования, например, с транспортами TCP и SSL).210211#### `class asyncio.DatagramProtocol`212213Базовый класс для реализации дейтаграммных протоколов (для использования, например, с транспортами UDP).214215#### `class asyncio.SubprocessProtocol`216217Базовый класс для реализации протоколов, взаимодействующих с дочерними процессами (через набор однонаправленных каналов).218219### 18.5.4.2.2. Колбэки соединения220221Эти колбэки могут вызываться для экземпляров [`Protocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.Protocol), [`DatagramProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol):222223#### `BaseProtocol.connection_made(transport)`224225Вызывается при установлении соединения.226227Аргумент *транспорт* представляет соединение. Если он понадобится, его следует сохранить (например, как атрибут).228229#### `BaseProtocol.connection_lost(exc)`230231Вызывается при потере или закрытии соединения.232233The argument is either an exception object or [`None`](https://python-all.ru/3.5/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.234235[`connection_made()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_made) и [`connection_lost()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost) вызываются ровно один раз за каждое успешное соединение. Все остальные колбэки вызываются между этими двумя методами, что упрощает управление ресурсами в реализации протокола.236237Следующие колбэки могут вызываться только для экземпляров [`SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol):238239#### `SubprocessProtocol.pipe_data_received(fd, data)`240241Вызывается, когда дочерний процесс записывает данные в свой канал stdout или stderr. *fd* – это целочисленный файловый дескриптор канала. *data* – это непустой объект bytes, содержащий данные.242243#### `SubprocessProtocol.pipe_connection_lost(fd, exc)`244245Вызывается, когда один из каналов, связывающих с дочерним процессом, закрывается. *fd* – это целочисленный файловый дескриптор закрытого канала.246247#### `SubprocessProtocol.process_exited()`248249Вызывается, когда дочерний процесс завершился.250251### 18.5.4.2.3. Потоковые протоколы252253Следующие колбэки вызываются для экземпляров [`Protocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.Protocol):254255#### `Protocol.data_received(data)`256257Вызывается при получении данных. *data* – это непустой объект bytes, содержащий входящие данные.258259> **Примечание**260>261> Буферизация, фрагментация или сборка данных зависит от транспорта. В общем случае не следует полагаться на конкретную семантику, а делать разбор достаточно общим и гибким. Однако данные всегда принимаются в правильном порядке.262263#### `Protocol.eof_received()`264265Вызывается, когда удалённая сторона сигнализирует, что больше не будет отправлять данные (например, вызовом `write_eof()`, если другая сторона также использует asyncio).266267Этот метод может вернуть ложное значение (включая `None`), и в этом случае транспорт закроется сам. Если же метод возвращает истинное значение, закрытие транспорта остаётся на усмотрение протокола. Поскольку реализация по умолчанию возвращает `None`, соединение неявно закрывается.268269> **Примечание**270>271> Некоторые транспорты, такие как SSL, не поддерживают полузакрытые соединения; в этом случае возврат истинного значения из метода не предотвращает закрытие соединения.272273`data_received()` может вызываться произвольное количество раз в течение соединения. Однако `eof_received()` вызывается не более одного раза, и после его вызова `data_received()` уже не будет вызван.274275Автомат состояний:276277> start -\>278>279> [`connection_made()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_made)280>281> \[-\>282>283> [`data_received()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.Protocol.data_received)284>285> \*\] \[-\>286>287> [`eof_received()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.Protocol.eof_received)288>289> ?\] -\>290>291> [`connection_lost()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.BaseProtocol.connection_lost)292>293> -\> end294295### 18.5.4.2.4. Протоколы дейтаграмм296297Следующие колбэки вызываются для экземпляров [`DatagramProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.DatagramProtocol).298299#### `DatagramProtocol.datagram_received(data, addr)`300301Вызывается при получении дейтаграммы. *data* – это байтовый объект, содержащий входящие данные. *addr* – адрес узла, отправляющего данные; точный формат зависит от транспорта.302303#### `DatagramProtocol.error_received(exc)`304305Вызывается, когда предыдущая операция отправки или получения вызывает исключение [`OSError`](https://python-all.ru/3.5/library/exceptions.html#OSError). *exc* – экземпляр [`OSError`](https://python-all.ru/3.5/library/exceptions.html#OSError).306307Этот метод вызывается в редких случаях, когда транспорт (например, UDP) обнаруживает, что дейтаграмма не может быть доставлена получателю. Однако во многих случаях недоставленные дейтаграммы просто молча отбрасываются.308309### 18.5.4.2.5. Колбэки управления потоком310311Эти колбэки могут вызываться для экземпляров [`Protocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.Protocol), [`DatagramProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.DatagramProtocol) и [`SubprocessProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.SubprocessProtocol):312313#### `BaseProtocol.pause_writing()`314315Вызывается, когда буфер транспорта превышает верхнюю границу (high-water mark).316317#### `BaseProtocol.resume_writing()`318319Вызывается, когда буфер транспорта опускается ниже нижней границы (low-water mark).320321Вызовы `pause_writing()` и `resume_writing()` парные – `pause_writing()` вызывается один раз, когда буфер строго превышает верхнюю границу (даже если последующие записи ещё больше увеличивают размер буфера), и затем `resume_writing()` вызывается один раз, когда размер буфера достигает нижней границы.322323> **Примечание**324>325> Если размер буфера равен верхней границе, `pause_writing()` не вызывается – он должен быть строго выше. И наоборот, `resume_writing()` вызывается, когда размер буфера меньше или равен нижней границе. Эти граничные условия важны, чтобы всё работало ожидаемым образом, когда одна из границ равна нулю.326327> **Примечание**328>329> В системах BSD (OS X, FreeBSD и т.д.) управление потоком не поддерживается для [`DatagramProtocol`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.DatagramProtocol), так как ошибки отправки из-за записи слишком большого количества пакетов трудно обнаружить. Сокет всегда кажется «готовым», и лишние пакеты отбрасываются; исключение [`OSError`](https://python-all.ru/3.5/library/exceptions.html#OSError) с errno, установленным в [`errno.ENOBUFS`](https://python-all.ru/3.5/library/errno.html#errno.ENOBUFS), может быть возбуждено или нет; если оно возбуждено, оно будет сообщено в [`DatagramProtocol.error_received()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.DatagramProtocol.error_received), но в остальном игнорируется.330331### 18.5.4.2.6. Корутины и протоколы332333Корутины могут быть запланированы в методе протокола с помощью [`ensure_future()`](https://python-all.ru/3.5/library/asyncio-task.html#asyncio.ensure_future), но порядок выполнения не гарантируется. Протоколы не знают о корутинах, созданных в их методах, и поэтому не будут ждать их.334335Для надёжного порядка выполнения используйте [потоковые объекты](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio-streams) в корутине с `yield from`. Например, корутину [`StreamWriter.drain()`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.StreamWriter.drain) можно использовать для ожидания, пока буфер записи не будет сброшен.336337## 18.5.4.3. Примеры протоколов338339### 18.5.4.3.1. Протокол TCP-клиента echo340341TCP-клиент эхо, использующий метод [`AbstractEventLoop.create_connection()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_connection), отправляет данные и ждёт, пока соединение не будет закрыто:342343```python344import asyncio345346class EchoClientProtocol(asyncio.Protocol):347    def __init__(self, message, loop):348        self.message = message349        self.loop = loop350351    def connection_made(self, transport):352        transport.write(self.message.encode())353        print('Data sent: {!r}'.format(self.message))354355    def data_received(self, data):356        print('Data received: {!r}'.format(data.decode()))357358    def connection_lost(self, exc):359        print('The server closed the connection')360        print('Stop the event loop')361        self.loop.stop()362363loop = asyncio.get_event_loop()364message = 'Hello World!'365coro = loop.create_connection(lambda: EchoClientProtocol(message, loop),366                              '127.0.0.1', 8888)367loop.run_until_complete(coro)368loop.run_forever()369loop.close()370```371372Цикл событий запускается дважды. Метод [`run_until_complete()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.run_until_complete) предпочтительнее в этом коротком примере, чтобы вызвать исключение, если сервер не прослушивается, вместо того чтобы писать короткую корутину для обработки исключения и остановки работающего цикла. При выходе [`run_until_complete()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.run_until_complete) цикл больше не работает, поэтому нет необходимости останавливать цикл в случае ошибки.373374> **См. также**375>376> Пример [TCP-эхо-клиента с использованием потоков данных](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio-tcp-echo-client-streams) использует функцию [`asyncio.open_connection()`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.open_connection).377378### 18.5.4.3.2. Протокол TCP-сервера echo379380TCP-эхо-сервер с использованием метода [`AbstractEventLoop.create_server()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_server), отправляет обратно полученные данные и закрывает соединение:381382```python383import asyncio384385class EchoServerClientProtocol(asyncio.Protocol):386    def connection_made(self, transport):387        peername = transport.get_extra_info('peername')388        print('Connection from {}'.format(peername))389        self.transport = transport390391    def data_received(self, data):392        message = data.decode()393        print('Data received: {!r}'.format(message))394395        print('Send: {!r}'.format(message))396        self.transport.write(data)397398        print('Close the client socket')399        self.transport.close()400401loop = asyncio.get_event_loop()402# Каждое клиентское соединение создаёт новый экземпляр протокола.403coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)404server = loop.run_until_complete(coro)405406# Обслуживать запросы до нажатия Ctrl+C407print('Serving on {}'.format(server.sockets[0].getsockname()))408try:409    loop.run_forever()410except KeyboardInterrupt:411    pass412413# Закрыть сервер414server.close()415loop.run_until_complete(server.wait_closed())416loop.close()417```418419`Transport.close()` можно вызвать сразу после [`WriteTransport.write()`](https://python-all.ru/3.5/library/asyncio-protocol.html#asyncio.WriteTransport.write), даже если данные еще не отправлены на сокет: оба метода асинхронны. `yield from` не нужен, поскольку эти методы транспорта не являются корутинами.420421> **См. также**422>423> Пример [TCP-эхо-сервера с использованием потоков данных](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio-tcp-echo-server-streams) использует функцию [`asyncio.start_server()`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.start_server).424425### 18.5.4.3.3. Протокол UDP-эхо-клиента426427UDP-эхо-клиент с использованием метода [`AbstractEventLoop.create_datagram_endpoint()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_datagram_endpoint) отправляет данные и закрывает транспорт при получении ответа:428429```python430import asyncio431432class EchoClientProtocol:433    def __init__(self, message, loop):434        self.message = message435        self.loop = loop436        self.transport = None437438    def connection_made(self, transport):439        self.transport = transport440        print('Send:', self.message)441        self.transport.sendto(self.message.encode())442443    def datagram_received(self, data, addr):444        print("Received:", data.decode())445446        print("Close the socket")447        self.transport.close()448449    def error_received(self, exc):450        print('Error received:', exc)451452    def connection_lost(self, exc):453        print("Socket closed, stop the event loop")454        loop = asyncio.get_event_loop()455        loop.stop()456457loop = asyncio.get_event_loop()458message = "Hello World!"459connect = loop.create_datagram_endpoint(460    lambda: EchoClientProtocol(message, loop),461    remote_addr=('127.0.0.1', 9999))462transport, protocol = loop.run_until_complete(connect)463loop.run_forever()464transport.close()465loop.close()466```467468### 18.5.4.3.4. Протокол UDP-эхо-сервера469470UDP-эхо-сервер с использованием метода [`AbstractEventLoop.create_datagram_endpoint()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_datagram_endpoint) отправляет обратно полученные данные:471472```python473import asyncio474475class EchoServerProtocol:476    def connection_made(self, transport):477        self.transport = transport478479    def datagram_received(self, data, addr):480        message = data.decode()481        print('Received %r from %s' % (message, addr))482        print('Send %r to %s' % (message, addr))483        self.transport.sendto(data, addr)484485loop = asyncio.get_event_loop()486print("Starting UDP server")487# Будет создан один экземпляр протокола для обслуживания всех клиентских запросов.488listen = loop.create_datagram_endpoint(489    EchoServerProtocol, local_addr=('127.0.0.1', 9999))490transport, protocol = loop.run_until_complete(listen)491492try:493    loop.run_forever()494except KeyboardInterrupt:495    pass496497transport.close()498loop.close()499```500501### 18.5.4.3.5. Регистрация открытого сокета для ожидания данных с помощью протокола502503Ожидать, пока сокет получит данные, используя метод [`AbstractEventLoop.create_connection()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.create_connection) с протоколом, а затем закрыть цикл событий504505```python506import asyncio507try:508    from socket import socketpair509except ImportError:510    from asyncio.windows_utils import socketpair511512# Создаём пару соединённых сокетов513rsock, wsock = socketpair()514loop = asyncio.get_event_loop()515516class MyProtocol(asyncio.Protocol):517    transport = None518519    def connection_made(self, transport):520        self.transport = transport521522    def data_received(self, data):523        print("Received:", data.decode())524525        # Готово: закрыть транспорт (он вызовет connection_lost()).526        self.transport.close()527528    def connection_lost(self, exc):529        # Сокет закрыт, остановить цикл событий.530        loop.stop()531532# Зарегистрировать сокет для ожидания данных.533connect_coro = loop.create_connection(MyProtocol, sock=rsock)534transport, protocol = loop.run_until_complete(connect_coro)535536# Симулировать приём данных из сети537loop.call_soon(wsock.send, 'abc'.encode())538539# Запустить цикл событий540loop.run_forever()541542# Готово, закрыть сокеты и цикл событий.543rsock.close()544wsock.close()545loop.close()546```547548> **См. также**549>550> Пример [наблюдения за файловым дескриптором на предмет событий чтения](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio-watch-read-event) использует низкоуровневый метод [`AbstractEventLoop.add_reader()`](https://python-all.ru/3.5/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.add_reader) для регистрации файлового дескриптора сокета.551>552> В примере [регистрации открытого сокета для ожидания данных с помощью потоков](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio-register-socket-streams) используются высокоуровневые потоки , созданные функцией [`open_connection()`](https://python-all.ru/3.5/library/asyncio-stream.html#asyncio.open_connection) в корутине.553