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

asyncore.md

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

1> **Источник:** https://python-all.ru/3.2/library/asyncore.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.5. [`asyncore`](https://python-all.ru/3.2/library/asyncore.html#module-asyncore) – Асинхронный обработчик сокетов89**Исходный код:** [Lib/asyncore.py](https://python-all.ru/src/3.2/Lib/asyncore.py)1011---1213Этот модуль предоставляет базовую инфраструктуру для написания клиентов и серверов асинхронных сокетных служб.1415Есть только два способа заставить программу на одном процессоре делать «более одного дела одновременно». Многопоточное программирование – самый простой и популярный способ, но есть и другой, совершенно иной метод, который даёт почти все преимущества многопоточности без использования нескольких потоков. Он действительно практичен только в том случае, если программа в основном связана с вводом-выводом. Если программа загружает процессор, то, вероятно, на самом деле требуются вытесняющие потоки с планированием. Однако сетевые серверы редко бывают привязаны к процессору.1617Если ваша операционная система поддерживает системный вызов `select()` в библиотеке ввода-вывода (а поддерживают почти все), вы можете использовать его для одновременной работы с несколькими каналами связи, выполняя другую работу, пока ввод-вывод происходит в «фоновом режиме». Хотя такой подход может показаться странным и сложным, особенно на первых порах, во многих отношениях он проще для понимания и управления, чем многопоточное программирование. Модуль [`asyncore`](https://python-all.ru/3.2/library/asyncore.html#module-asyncore) решает многие сложные проблемы, делая построение сложных высокопроизводительных сетевых серверов и клиентов простым делом. Для «диалоговых» приложений и протоколов незаменим сопутствующий модуль [`asynchat`](https://python-all.ru/3.2/library/asynchat.html#module-asynchat).1819Основная идея обоих модулей – создать один или несколько сетевых *каналов*, экземпляров классов [`asyncore.dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher) и [`asynchat.async_chat`](https://python-all.ru/3.2/library/asynchat.html#asynchat.async_chat). Создание каналов добавляет их в глобальную карту, используемую функцией [`loop()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.loop), если вы не предоставите ей собственную *карту*.2021После создания начального канала (или каналов) вызов функции [`loop()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.loop) активирует обслуживание каналов, которое продолжается до тех пор, пока последний канал (включая те, что были добавлены в карту во время асинхронного обслуживания) не будет закрыт.2223#### `asyncore.loop([timeout[, use_poll[, map[, count]]]])`2425Запускает цикл опроса, который завершается после count проходов или когда все открытые каналы будут закрыты. Все аргументы необязательны. Параметр *count* по умолчанию равен None, в результате цикл завершается только тогда, когда все каналы закрыты. Аргумент *timeout* задаёт параметр тайм-аута для соответствующего вызова [`select()`](https://python-all.ru/3.2/library/select.html#module-select) или `poll()`, измеряется в секундах; значение по умолчанию – 30 секунд. Параметр *use\_poll*, если равен true, указывает, что следует использовать `poll()` вместо [`select()`](https://python-all.ru/3.2/library/select.html#module-select) (по умолчанию `False`).2627The *map* parameter is a dictionary whose items are the channels to watch. As channels are closed they are deleted from their map. If *map* is omitted, a global map is used. Channels (instances of [`asyncore.dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher), [`asynchat.async_chat`](https://python-all.ru/3.2/library/asynchat.html#asynchat.async_chat) and subclasses thereof) can freely be mixed in the map.2829#### `class asyncore.dispatcher`3031Класс [`dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher) – это тонкая обёртка вокруг низкоуровневого сокетного объекта. Чтобы сделать его более полезным, у него есть несколько методов для обработки событий, которые вызываются из асинхронного цикла. В остальном его можно рассматривать как обычный неблокирующий сокетный объект.3233Возникновение низкоуровневых событий в определённые моменты или в определённых состояниях соединения сообщает асинхронному циклу, что произошли некоторые высокоуровневые события. Например, если мы запросили подключение сокета к другому хосту, мы знаем, что соединение установлено, когда сокет впервые становится доступным для записи (в этот момент можно писать в него, рассчитывая на успех). Подразумеваемые высокоуровневые события:3435| Событие | Описание |36| --- | --- |37| `handle_connect()` | Подразумевается первым событием чтения или записи |38| `handle_close()` | Подразумевается событием чтения, когда данные недоступны |39| `handle_accepted()` | Подразумевается событием чтения на слушающем сокете |4041Во время асинхронной обработки методы [`readable()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher.readable) и [`writable()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher.writable) каждого сопоставленного канала используются для определения, нужно ли добавить сокет канала в список каналов, для которых `select()` или `poll()` ожидают события чтения и записи.4243Таким образом, набор событий канала шире, чем базовые события сокета. Полный набор методов, которые можно переопределить в вашем подклассе, приведён ниже:4445#### `handle_read()`4647Вызывается, когда асинхронный цикл обнаруживает, что вызов `read()` на сокете канала завершится успешно.4849#### `handle_write()`5051Вызывается, когда асинхронный цикл обнаруживает, что сокет, доступный для записи, можно записать. Часто этот метод реализует необходимую буферизацию для производительности. Например:5253```python54def handle_write(self):55    sent = self.send(self.buffer)56    self.buffer = self.buffer[sent:]57```5859#### `handle_expt()`6061Вызывается при наличии внеполосных данных (OOB) для сокетного соединения. Это почти никогда не происходит, так как OOB поддерживается слабо и редко используется.6263#### `handle_connect()`6465Вызывается, когда сокет активного инициатора действительно устанавливает соединение. Может, например, отправить приветственный баннер или инициировать согласование протокола с удалённой конечной точкой.6667#### `handle_close()`6869Вызывается при закрытии сокета.7071#### `handle_error()`7273Вызывается, когда возникает исключение, которое иначе не обрабатывается. Версия по умолчанию выводит сокращённую трассировку.7475#### `handle_accept()`7677Вызывается на прослушивающих каналах (пассивных открывателях), когда может быть установлено соединение с новой удалённой конечной точкой, которая выполнила вызов [`connect()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher.connect) для локальной конечной точки. Устарело в версии 3.2; используйте [`handle_accepted()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher.handle_accepted) вместо него.7879Устарело с версии 3.2.8081#### `handle_accepted(sock, addr)`8283Вызывается на прослушивающих каналах (пассивных открывателях), когда установлено соединение с новой удалённой конечной точкой, которая выполнила вызов [`connect()`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher.connect) для локальной конечной точки. *sock* – это *новый* сокетный объект, пригодный для отправки и приёма данных по соединению, а *addr* – адрес, привязанный к сокету на другом конце соединения.8485Новое в версии 3.2.8687#### `readable()`8889Вызывается на каждом проходе асинхронного цикла, чтобы определить, нужно ли добавить сокет канала в список, на котором могут происходить события чтения. Метод по умолчанию просто возвращает `True`, указывая, что по умолчанию все каналы заинтересованы в событиях чтения.9091#### `writable()`9293Вызывается на каждом проходе асинхронного цикла, чтобы определить, нужно ли добавить сокет канала в список, на котором могут происходить события записи. Метод по умолчанию просто возвращает `True`, указывая, что по умолчанию все каналы заинтересованы в событиях записи.9495Кроме того, каждый канал делегирует или расширяет многие методы сокета. Большинство из них почти идентичны своим сокетным аналогам.9697#### `create_socket(family, type)`9899Это идентично созданию обычного сокета и использует те же параметры создания. Обратитесь к документации [`socket`](https://python-all.ru/3.2/library/socket.html#module-socket) за информацией о создании сокетов.100101#### `connect(address)`102103Как и для обычного объекта сокета, *address* – это кортеж, первый элемент которого – хост для подключения, а второй – номер порта.104105#### `send(data)`106107Отправляет *data* на удалённый узел сокета.108109#### `recv(buffer_size)`110111Читает не более *buffer\_size* байт из удалённой конечной точки сокета. Пустая строка означает, что канал был закрыт с другого конца.112113#### `listen(backlog)`114115Ожидает входящие соединения на сокете. Аргумент *backlog* задаёт максимальное количество соединений в очереди и должен быть не менее 1; максимальное значение зависит от системы (обычно 5).116117#### `bind(address)`118119Привязывает сокет к адресу *address*. Сокет не должен быть уже привязан. (Формат *address* зависит от семейства адресов – обратитесь к документации [`socket`](https://python-all.ru/3.2/library/socket.html#module-socket) для получения дополнительной информации.) Чтобы пометить сокет как повторно используемый (установив параметр `SO_REUSEADDR`), вызовите метод объекта [`dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher) `set_reuse_addr()`.120121#### `accept()`122123Принимает соединение. Сокет должен быть привязан к адресу и ожидать соединений. Возвращаемое значение может быть либо `None`, либо пара `(conn, address)`, где *conn* – это *новый* объект сокета, который можно использовать для отправки и получения данных по соединению, а *address* – это адрес, привязанный к сокету на другом конце соединения. Когда возвращается `None`, это означает, что соединение не состоялось; в этом случае серверу следует просто игнорировать это событие и продолжать ожидать последующие входящие соединения.124125#### `close()`126127Закрывает сокет. Все последующие операции с объектом сокета будут завершаться ошибкой. Удалённый узел больше не будет получать данные (после отправки данных из очереди). Сокеты автоматически закрываются при сборке мусора.128129#### `class asyncore.dispatcher_with_send`130131Подкласс [`dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher), который добавляет простую возможность буферизованного вывода, полезен для простых клиентов. Для более сложного использования применяйте [`asynchat.async_chat`](https://python-all.ru/3.2/library/asynchat.html#asynchat.async_chat).132133#### `class asyncore.file_dispatcher`134135file\_dispatcher принимает файловый дескриптор или [*объект файла*](https://python-all.ru/3.2/glossary.html#term-file-object) вместе с необязательным аргументом map и оборачивает его для использования с функциями `poll()` или `loop()`. Если передан файловый объект или что-либо с методом `fileno()`, этот метод будет вызван, а результат передан конструктору [`file_wrapper`](https://python-all.ru/3.2/library/asyncore.html#asyncore.file_wrapper). Доступно: UNIX.136137#### `class asyncore.file_wrapper`138139file\_wrapper принимает целочисленный файловый дескриптор и вызывает [`os.dup()`](https://python-all.ru/3.2/library/os.html#os.dup) для дублирования дескриптора, чтобы исходный дескриптор можно было закрыть независимо от file\_wrapper. Этот класс реализует достаточное количество методов для эмуляции сокета для использования классом [`file_dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.file_dispatcher). Доступно: UNIX.140141## 17.5.1. Пример asyncore: базовый HTTP-клиент142143Вот очень простой HTTP-клиент, который использует класс [`dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher) для реализации обработки сокетов:144145```python146import asyncore, socket147148class HTTPClient(asyncore.dispatcher):149150    def __init__(self, host, path):151        asyncore.dispatcher.__init__(self)152        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)153        self.connect( (host, 80) )154        self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %155                            (path, host), 'ascii')156157    def handle_connect(self):158        pass159160    def handle_close(self):161        self.close()162163    def handle_read(self):164        print(self.recv(8192))165166    def writable(self):167        return (len(self.buffer) > 0)168169    def handle_write(self):170        sent = self.send(self.buffer)171        self.buffer = self.buffer[sent:]172173 client = HTTPClient('www.python.org', '/')174 asyncore.loop()175```176177## 17.5.2. asyncore Пример базового эхо-сервера178179Вот простой эхо-сервер, который использует класс [`dispatcher`](https://python-all.ru/3.2/library/asyncore.html#asyncore.dispatcher) для приёма соединений и направляет входящие соединения обработчику:180181```python182import asyncore183import socket184185class EchoHandler(asyncore.dispatcher_with_send):186187    def handle_read(self):188        data = self.recv(8192)189        if data:190            self.send(data)191192class EchoServer(asyncore.dispatcher):193194    def __init__(self, host, port):195        asyncore.dispatcher.__init__(self)196        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)197        self.set_reuse_addr()198        self.bind((host, port))199        self.listen(5)200201    def handle_accepted(self, sock, addr):202        print('Incoming connection from %s' % repr(addr))203        handler = EchoHandler(sock)204205server = EchoServer('localhost', 8080)206asyncore.loop()207```208