asyncore.md
1> **Источник:** https://python-all.ru/3.1/library/asyncore.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.5. `asyncore` – асинхронный обработчик сокетов89Этот модуль предоставляет базовую инфраструктуру для написания клиентов и серверов асинхронных сокетных служб.1011Есть только два способа заставить программу на одном процессоре делать «более одного дела одновременно». Многопоточное программирование – самый простой и популярный способ, но есть и другой, совершенно иной метод, который даёт почти все преимущества многопоточности без использования нескольких потоков. Он действительно практичен только в том случае, если программа в основном связана с вводом-выводом. Если программа загружает процессор, то, вероятно, на самом деле требуются вытесняющие потоки с планированием. Однако сетевые серверы редко бывают привязаны к процессору.1213Если ваша операционная система поддерживает системный вызов `select()` в библиотеке ввода-вывода (а такие есть почти во всех), то с его помощью можно одновременно обрабатывать несколько каналов связи, выполняя другую работу, пока ввод-вывод происходит «в фоне». Хотя такой подход может показаться странным и сложным, особенно на первых порах, во многих отношениях он проще для понимания и управления, чем многопоточное программирование. Модуль `asyncore` решает многие сложные проблемы, позволяя без труда создавать сложные высокопроизводительные сетевые серверы и клиенты. Для «диалоговых» приложений и протоколов незаменим сопутствующий модуль [`asynchat`](https://python-all.ru/3.1/library/asynchat.html#module-asynchat).1415Основная идея обоих модулей – создать один или несколько сетевых *каналов*, экземпляров классов [`asyncore.dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher) и [`asynchat.async_chat`](https://python-all.ru/3.1/library/asynchat.html#asynchat.async_chat). Создание каналов добавляет их в глобальную карту, используемую функцией [`loop()`](https://python-all.ru/3.1/library/asyncore.html#asyncore.loop), если вы не предоставите ей собственную *карту*.1617После создания начального канала (или каналов) вызов функции [`loop()`](https://python-all.ru/3.1/library/asyncore.html#asyncore.loop) активирует обслуживание каналов, которое продолжается до тех пор, пока последний канал (включая те, что были добавлены в карту во время асинхронного обслуживания) не будет закрыт.1819#### `asyncore.loop([timeout[, use_poll[, map[, count]]]])`2021Enter a polling loop that terminates after count passes or all open channels have been closed. All arguments are optional. The *count* parameter defaults to None, resulting in the loop terminating only when all channels have been closed. The *timeout* argument sets the timeout parameter for the appropriate `select()` or `poll()` call, measured in seconds; the default is 30 seconds. The *use\_poll* parameter, if true, indicates that `poll()` should be used in preference to `select()` (the default is `False`).2223The *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.1/library/asyncore.html#asyncore.dispatcher), [`asynchat.async_chat`](https://python-all.ru/3.1/library/asynchat.html#asynchat.async_chat) and subclasses thereof) can freely be mixed in the map.2425#### `class asyncore.dispatcher`2627Класс [`dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher) – это тонкая обёртка вокруг низкоуровневого сокетного объекта. Чтобы сделать его более полезным, у него есть несколько методов для обработки событий, которые вызываются из асинхронного цикла. В остальном его можно рассматривать как обычный неблокирующий сокетный объект.2829Возникновение низкоуровневых событий в определённые моменты или в определённых состояниях соединения сообщает асинхронному циклу, что произошли некоторые высокоуровневые события. Например, если мы запросили подключение сокета к другому хосту, мы знаем, что соединение установлено, когда сокет впервые становится доступным для записи (в этот момент можно писать в него, рассчитывая на успех). Подразумеваемые высокоуровневые события:3031| Событие | Описание |32| --- | --- |33| `handle_connect()` | Подразумевается первым событием чтения или записи |34| `handle_close()` | Подразумевается событием чтения, когда данные недоступны |35| `handle_accept()` | Подразумевается событием чтения на слушающем сокете |3637Во время асинхронной обработки методы [`readable()`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher.readable) и [`writable()`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher.writable) каждого сопоставленного канала используются для определения, нужно ли добавить сокет канала в список каналов, для которых `select()` или `poll()` ожидают события чтения и записи.3839Таким образом, набор событий канала шире, чем базовые события сокета. Полный набор методов, которые можно переопределить в вашем подклассе, приведён ниже:4041#### `handle_read()`4243Вызывается, когда асинхронный цикл обнаруживает, что вызов4445`read()`4647на сокете канала завершится успешно.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Вызывается на прослушивающих каналах (пассивных открывателях), когда можно установить соединение с новой удалённой конечной точкой, которая выполнила вызов7879[`connect()`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher.connect)8081для данной локальной конечной точки.8283#### `readable()`8485Вызывается на каждом проходе асинхронного цикла, чтобы определить, нужно ли добавить сокет канала в список, на котором могут происходить события чтения. Метод по умолчанию просто возвращает8687`True`8889, указывая, что по умолчанию все каналы заинтересованы в событиях чтения.9091#### `writable()`9293Вызывается на каждом проходе асинхронного цикла, чтобы определить, нужно ли добавить сокет канала в список, на котором могут происходить события записи. Метод по умолчанию просто возвращает9495`True`9697, указывая, что по умолчанию все каналы заинтересованы в событиях записи.9899Кроме того, каждый канал делегирует или расширяет многие методы сокета. Большинство из них почти идентичны своим сокетным аналогам.100101#### `create_socket(family, type)`102103Это идентично созданию обычного сокета и использует те же параметры создания. Обратитесь к документации104105[`socket`](https://python-all.ru/3.1/library/socket.html#module-socket)106107за информацией о создании сокетов.108109#### `connect(address)`110111Как и для обычного объекта сокета,112113*address*114115– это кортеж, первый элемент которого – хост для подключения, а второй – номер порта.116117#### `send(data)`118119Отправляет120121*data*122123на удалённый узел сокета.124125#### `recv(buffer_size)`126127Читает не более128129*buffer\_size*130131байт из удалённой конечной точки сокета. Пустая строка означает, что канал был закрыт с другого конца.132133#### `listen(backlog)`134135Ожидает входящие соединения на сокете. Аргумент136137*backlog*138139задаёт максимальное количество соединений в очереди и должен быть не менее 1; максимальное значение зависит от системы (обычно 5).140141#### `bind(address)`142143Привязывает сокет к адресу144145*address*146147. Сокет не должен быть уже привязан. (Формат148149*address*150151зависит от семейства адресов – см. выше.) Чтобы пометить сокет как повторно используемый (установив опцию152153`SO_REUSEADDR`154155), вызовите у объекта156157[`dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher)158159метод160161`set_reuse_addr()`162163.164165#### `accept()`166167Принимает соединение. Сокет должен быть привязан к адресу и ожидать соединений. Возвращаемое значение может быть либо168169`None`170171, либо пара172173`(conn, address)`174175, где176177*conn*178179– это180181*новый*182183объект сокета, который можно использовать для отправки и получения данных по соединению, а184185*address*186187– это адрес, привязанный к сокету на другом конце соединения. Когда возвращается188189`None`190191, это означает, что соединение не состоялось; в этом случае серверу следует просто игнорировать это событие и продолжать ожидать последующие входящие соединения.192193#### `close()`194195Закрывает сокет. Все последующие операции с объектом сокета будут завершаться ошибкой. Удалённый узел больше не будет получать данные (после отправки данных из очереди). Сокеты автоматически закрываются при сборке мусора.196197#### `class asyncore.dispatcher_with_send`198199Подкласс200201[`dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher)202203, который добавляет простую возможность буферизованного вывода, полезен для простых клиентов. Для более сложного использования применяйте204205[`asynchat.async_chat`](https://python-all.ru/3.1/library/asynchat.html#asynchat.async_chat)206207.208209#### `class asyncore.file_dispatcher`210211file\_dispatcher принимает файловый дескриптор или212213[*объект файла*](https://python-all.ru/3.1/glossary.html#term-file-object)214215вместе с необязательным аргументом map и оборачивает его для использования с функциями216217`poll()`218219или220221`loop()`222223. Если передан файловый объект или что-либо с методом224225`fileno()`226227, этот метод будет вызван, а результат передан конструктору228229[`file_wrapper`](https://python-all.ru/3.1/library/asyncore.html#asyncore.file_wrapper)230231. Доступно: UNIX.232233#### `class asyncore.file_wrapper`234235file\_wrapper принимает целочисленный файловый дескриптор и вызывает236237[`os.dup()`](https://python-all.ru/3.1/library/os.html#os.dup)238239для дублирования дескриптора, чтобы исходный дескриптор можно было закрыть независимо от file\_wrapper. Этот класс реализует достаточное количество методов для эмуляции сокета для использования классом240241[`file_dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.file_dispatcher)242243. Доступно: UNIX.244245## 17.5.1. Пример asyncore: базовый HTTP-клиент246247Вот очень простой HTTP-клиент, который использует класс [`dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher) для реализации обработки сокетов:248249```python250import asyncore, socket251252class HTTPClient(asyncore.dispatcher):253254 def __init__(self, host, path):255 asyncore.dispatcher.__init__(self)256 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)257 self.connect( (host, 80) )258 self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %259 (path, host), 'ascii')260261 def handle_connect(self):262 pass263264 def handle_close(self):265 self.close()266267 def handle_read(self):268 print(self.recv(8192))269270 def writable(self):271 return (len(self.buffer) > 0)272273 def handle_write(self):274 sent = self.send(self.buffer)275 self.buffer = self.buffer[sent:]276277client = HTTPClient('www.python.org', '/')278asyncore.loop()279```280281## 17.5.2. asyncore Пример базового эхо-сервера282283Вот простой эхо-сервер, который использует класс [`dispatcher`](https://python-all.ru/3.1/library/asyncore.html#asyncore.dispatcher) для приёма соединений и направляет входящие соединения обработчику:284285```python286import asyncore287import socket288289class EchoHandler(asyncore.dispatcher_with_send):290291 def handle_read(self):292 data = self.recv(8192)293 self.send(data)294295class EchoServer(asyncore.dispatcher):296297 def __init__(self, host, port):298 asyncore.dispatcher.__init__(self)299 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)300 self.set_reuse_addr()301 self.bind((host, port))302 self.listen(5)303304 def handle_accept(self):305 pair = self.accept()306 if pair is None:307 return308 else:309 sock, addr = pair310 print('Incoming connection from %s' % repr(addr))311 handler = EchoHandler(sock)312313server = EchoServer('localhost', 8080)314asyncore.loop()315```316