asynchat.md
1> **Источник:** https://python-all.ru/3.0/library/asynchat.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `asynchat` – Асинхронный обработчик команд/ответов через сокеты89Этот модуль основывается на инфраструктуре [`asyncore`](https://python-all.ru/3.0/library/asyncore.html#module-asyncore), упрощая асинхронные клиенты и серверы и облегчая работу с протоколами, элементы которых завершаются произвольными строками или имеют переменную длину. `asynchat` определяет абстрактный класс [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat), который вы наследуете, реализуя методы `collect_incoming_data()` и `found_terminator()`. Он использует тот же асинхронный цикл, что и [`asyncore`](https://python-all.ru/3.0/library/asyncore.html#module-asyncore), и два типа каналов, [`asyncore.dispatcher`](https://python-all.ru/3.0/library/asyncore.html#asyncore.dispatcher) и [`asynchat.async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat), можно свободно смешивать в карте каналов. Обычно серверный канал [`asyncore.dispatcher`](https://python-all.ru/3.0/library/asyncore.html#asyncore.dispatcher) создаёт новые объекты канала [`asynchat.async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat) при получении входящих запросов на соединение.1011#### `[asynchat.async_chat]class asynchat.async_chat`1213Этот класс является абстрактным подклассом [`asyncore.dispatcher`](https://python-all.ru/3.0/library/asyncore.html#asyncore.dispatcher). Чтобы использовать код на практике, необходимо наследовать [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat), реализовав осмысленные методы [`collect_incoming_data()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator). Методы [`asyncore.dispatcher`](https://python-all.ru/3.0/library/asyncore.html#asyncore.dispatcher) могут использоваться, хотя не все имеют смысл в контексте сообщений/ответов.1415Как и [`asyncore.dispatcher`](https://python-all.ru/3.0/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat) определяет набор событий, которые генерируются анализом состояния сокета после вызова `select`. Как только запускается цикл опроса, методы объекта [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat) вызываются фреймворком обработки событий без каких-либо действий со стороны программиста.1617Два атрибута класса можно изменять для повышения производительности или, возможно, даже для экономии памяти.1819#### `[asynchat.async_chat.ac_in_buffer_size]ac_in_buffer_size`2021Размер асинхронного входного буфера (по умолчанию2223`4096`2425).2627#### `[asynchat.async_chat.ac_out_buffer_size]ac_out_buffer_size`2829Размер асинхронного выходного буфера (по умолчанию3031`4096`3233).3435В отличие от [`asyncore.dispatcher`](https://python-all.ru/3.0/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat) позволяет определить очередь «первым вошёл – первым вышел» (fifo) из *производителей*. У производителя должен быть только один метод: `more()`, который возвращает данные для передачи по каналу. Производитель сигнализирует об истощении (*т.е.* о том, что данных больше нет), когда его метод `more()` возвращает пустую строку. В этот момент объект [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat) удаляет производителя из fifo и начинает использовать следующего, если таковой имеется. Когда очередь производителей пуста, метод [`handle_write()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.handle_write) ничего не делает. Метод [`set_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.set_terminator) объекта канала используется для описания того, как распознать конец или важную точку останова во входящей передаче от удалённой конечной точки.3637Чтобы создать работающий подкласс [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat), ваши методы ввода [`collect_incoming_data()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator) должны обрабатывать данные, которые канал получает асинхронно. Методы описаны ниже.3839#### `[asynchat.async_chat.close_when_done]async_chat.close_when_done()`4041Помещает4243`None`4445в очередь производителей. Когда этот производитель извлекается из очереди, канал закрывается.4647#### `[asynchat.async_chat.collect_incoming_data]async_chat.collect_incoming_data(data)`4849Вызывается с5051*data*5253, содержащим произвольный объем полученных данных. Метод по умолчанию, который должен быть переопределен, вызывает исключение5455[`NotImplementedError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.NotImplementedError)5657.5859#### `[asynchat.async_chat._collect_incoming_data]async_chat._collect_incoming_data(data)`6061Пример реализации процедуры сбора данных для использования вместе с6263[`_get_data()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat._get_data)6465в задаваемой пользователем6667[`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator)6869.7071#### `[asynchat.async_chat.discard_buffers]async_chat.discard_buffers()`7273В экстренных случаях этот метод удаляет любые данные, находящиеся во входных и/или выходных буферах, а также в очереди производителей.7475#### `[asynchat.async_chat.found_terminator]async_chat.found_terminator()`7677Вызывается, когда входящий поток данных соответствует условию завершения, установленному7879[`set_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.set_terminator)8081. Метод по умолчанию, который должен быть переопределен, вызывает исключение8283[`NotImplementedError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.NotImplementedError)8485. Буферизованные входные данные должны быть доступны через атрибут экземпляра.8687#### `[asynchat.async_chat._get_data]async_chat._get_data()`8889Возвращает и очищает данные, полученные с помощью примера реализации9091[`_collect_incoming_data()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat._collect_incoming_data)9293.9495#### `[asynchat.async_chat.get_terminator]async_chat.get_terminator()`9697Возвращает текущий терминатор канала.9899#### `[asynchat.async_chat.handle_close]async_chat.handle_close()`100101Вызывается, когда канал закрыт. Метод по умолчанию молча закрывает сокет канала.102103#### `[asynchat.async_chat.handle_read]async_chat.handle_read()`104105Вызывается, когда в асинхронном цикле происходит событие чтения на сокете канала. Метод по умолчанию проверяет условие завершения, установленное106107[`set_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.set_terminator)108109, которое может быть либо появлением определённой строки во входном потоке, либо получением определённого количества символов. Когда терминатор найден,110111[`handle_read()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.handle_read)112113вызывает метод114115[`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator)116117после вызова118119[`collect_incoming_data()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.collect_incoming_data)120121с данными, предшествующими завершающему условию.122123#### `[asynchat.async_chat.handle_write]async_chat.handle_write()`124125Вызывается, когда приложение может записать данные в канал. Метод по умолчанию вызывает метод126127`initiate_send()`128129, который, в свою очередь, вызывает130131[`refill_buffer()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.refill_buffer)132133для сбора данных из очереди производителей, связанной с каналом.134135#### `[asynchat.async_chat.push]async_chat.push(data)`136137Создаёт объект138139[`simple_producer`](https://python-all.ru/3.0/library/asynchat.html#asynchat.simple_producer)140141(142143*см. ниже*144145), содержащий данные, и помещает его в очередь канала146147`producer_fifo`148149для обеспечения передачи. Это всё, что нужно сделать, чтобы канал записал данные в сеть, хотя можно использовать собственных производителей в более сложных схемах, например, для шифрования или разбиения на части.150151#### `[asynchat.async_chat.push_with_producer]async_chat.push_with_producer(producer)`152153Принимает объект производителя и добавляет его в очередь производителей, связанную с каналом. Когда все ранее помещенные производители будут исчерпаны, канал начнет потреблять данные этого производителя, вызывая его метод154155`more()`156157, и отправлять данные удаленной стороне.158159#### `[asynchat.async_chat.readable]async_chat.readable()`160161Должен возвращать162163`True`164165, чтобы канал был включён в набор каналов, проверяемых циклом166167`select`168169на читаемость.170171#### `[asynchat.async_chat.refill_buffer]async_chat.refill_buffer()`172173Пополняет выходной буфер, вызывая метод174175`more()`176177производителя в начале fifo. Если он истощён, производитель извлекается из fifo и активируется следующий. Если текущий производитель равен или становится178179`None`180181, канал закрывается.182183#### `[asynchat.async_chat.set_terminator]async_chat.set_terminator(term)`184185Устанавливает условие завершения для распознавания на канале. `term` может быть значением одного из трех типов, соответствующих трем различным способам обработки входящих протокольных данных.186187| терминатор | Описание |188| --- | --- |189| *строка* | Будет вызывать [`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator), когда строка будет найдена во входном потоке. |190| *целое* | Будет вызывать [`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator), когда будет получено указанное количество символов. |191| `None` | Канал продолжает собирать данные бесконечно |192193Обратите внимание, что любые данные, следующие за терминатором, будут доступны для чтения каналом после вызова [`found_terminator()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat.found_terminator).194195#### `[asynchat.async_chat.writable]async_chat.writable()`196197Должен возвращать198199`True`200201, пока элементы остаются в очереди производителя (fifo) или канал подключен и его выходной буфер не пуст.202203## asynchat – вспомогательные классы и функции204205#### `[asynchat.simple_producer]class asynchat.simple_producer(data[, buffer_size=512])`206207[`simple_producer`](https://python-all.ru/3.0/library/asynchat.html#asynchat.simple_producer) принимает кусок данных и необязательный размер буфера. Повторные вызовы его метода [`more()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.simple_producer.more) возвращают последовательные куски данных, размер которых не превышает *buffer\_size*.208209#### `[asynchat.simple_producer.more]more()`210211Выдаёт следующий кусок информации от производителя или возвращает пустую строку.212213#### `[asynchat.fifo]class asynchat.fifo([list=None])`214215Каждый канал поддерживает [`fifo`](https://python-all.ru/3.0/library/asynchat.html#asynchat.fifo), содержащую данные, которые были помещены приложением, но ещё не извлечены для записи в канал. [`fifo`](https://python-all.ru/3.0/library/asynchat.html#asynchat.fifo) представляет собой список, используемый для хранения данных и/или производителей до тех пор, пока они не потребуются. Если аргумент *list* передан, он должен содержать производителей или элементы данных для записи в канал.216217#### `[asynchat.fifo.is_empty]is_empty()`218219Возвращает220221`True`222223, если и только если fifo пуст.224225#### `[asynchat.fifo.first]first()`226227Возвращает самый старый элемент из fifo, добавленный с помощью228229[`push()`](https://python-all.ru/3.0/library/asynchat.html#asynchat.fifo.push)230231.232233#### `[asynchat.fifo.push]push(data)`234235Добавляет переданные данные (строку или объект-производитель) в producer fifo.236237#### `[asynchat.fifo.pop]pop()`238239Если fifo не пуст, возвращает240241`True, first()`242243, удаляя извлечённый элемент. Если fifo пуст, возвращает244245`False, None`246247.248249Модуль `asynchat` также определяет одну служебную функцию, которая может быть полезна при работе с сетью и анализе текста.250251#### `[asynchat.find_prefix_at_end]asynchat.find_prefix_at_end(haystack, needle)`252253Возвращает254255`True`256257, если строка258259*haystack*260261заканчивается любым непустым префиксом строки262263*needle*264265.266267## Пример asynchat268269Следующий неполный пример показывает, как можно читать HTTP-запросы с помощью [`async_chat`](https://python-all.ru/3.0/library/asynchat.html#asynchat.async_chat). Веб-сервер может создавать объект `http_request_handler` для каждого входящего клиентского соединения. Обратите внимание, что изначально терминатор канала устанавливается таким образом, чтобы соответствовать пустой строке в конце HTTP-заголовков, и флаг указывает, что заголовки читаются.270271После чтения заголовков, если запрос имеет тип POST (что указывает на наличие дополнительных данных во входном потоке), то заголовок `Content-Length:` используется для установки числового терминатора, чтобы прочитать нужное количество данных из канала.272273Метод `handle_request()` вызывается после того, как все соответствующие входные данные были маршалированы, после установки терминатора канала в `None`, чтобы любые посторонние данные, отправленные веб-клиентом, игнорировались.274275```python276class http_request_handler(asynchat.async_chat):277278 def __init__(self, sock, addr, sessions, log):279 asynchat.async_chat.__init__(self, sock=sock)280 self.addr = addr281 self.sessions = sessions282 self.ibuffer = []283 self.obuffer = b""284 self.set_terminator(b"\r\n\r\n")285 self.reading_headers = True286 self.handling = False287 self.cgi_data = None288 self.log = log289290 def collect_incoming_data(self, data):291 """Buffer the data"""292 self.ibuffer.append(data)293294 def found_terminator(self):295 if self.reading_headers:296 self.reading_headers = False297 self.parse_headers("".join(self.ibuffer))298 self.ibuffer = []299 if self.op.upper() == b"POST":300 clen = self.headers.getheader("content-length")301 self.set_terminator(int(clen))302 else:303 self.handling = True304 self.set_terminator(None)305 self.handle_request()306 elif not self.handling:307 self.set_terminator(None) # browsers sometimes over-send308 self.cgi_data = parse(self.headers, b"".join(self.ibuffer))309 self.handling = True310 self.ibuffer = []311 self.handle_request()312```313