asynchat.md
1> **Источник:** https://python-all.ru/2.7/library/asynchat.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.7. [`asynchat`](https://python-all.ru/2.7/library/asynchat.html#module-asynchat) – Асинхронный обработчик команд/ответов через сокеты89**Исходный код:** [Lib/asynchat.py](https://python-all.ru/src/2.7/Lib/asynchat.py)1011---1213Этот модуль построен на основе инфраструктуры [`asyncore`](https://python-all.ru/2.7/library/asyncore.html#module-asyncore), упрощая реализацию асинхронных клиентов и серверов, а также обработку протоколов, элементы которых завершаются произвольными строками или имеют переменную длину. [`asynchat`](https://python-all.ru/2.7/library/asynchat.html#module-asynchat) определяет абстрактный класс [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat), который нужно наследовать, реализуя методы `collect_incoming_data()` и `found_terminator()`. Он использует тот же асинхронный цикл, что и [`asyncore`](https://python-all.ru/2.7/library/asyncore.html#module-asyncore), и два типа каналов – [`asyncore.dispatcher`](https://python-all.ru/2.7/library/asyncore.html#asyncore.dispatcher) и [`asynchat.async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat) – могут свободно сочетаться в карте каналов. Обычно серверный канал [`asyncore.dispatcher`](https://python-all.ru/2.7/library/asyncore.html#asyncore.dispatcher) создаёт новые объекты канала [`asynchat.async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat) при получении входящих запросов на соединение.1415#### `class asynchat.async_chat`1617Этот класс является абстрактным подклассом [`asyncore.dispatcher`](https://python-all.ru/2.7/library/asyncore.html#asyncore.dispatcher). Чтобы использовать его на практике, необходимо наследовать [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat), реализовав осмысленные методы [`collect_incoming_data()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.found_terminator). Методы [`asyncore.dispatcher`](https://python-all.ru/2.7/library/asyncore.html#asyncore.dispatcher) можно использовать, хотя не все из них имеют смысл в контексте сообщений/ответов.1819Как и [`asyncore.dispatcher`](https://python-all.ru/2.7/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat) определяет набор событий, порождаемых анализом состояния сокета после вызова `select()`. Как только цикл опроса запущен, методы объекта [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat) вызываются каркасом обработки событий без каких-либо действий со стороны программиста.2021Два атрибута класса можно изменять для повышения производительности или, возможно, даже для экономии памяти.2223#### `ac_in_buffer_size`2425Размер асинхронного входного буфера (по умолчанию `4096`).2627#### `ac_out_buffer_size`2829Размер асинхронного выходного буфера (по умолчанию `4096`).3031В отличие от [`asyncore.dispatcher`](https://python-all.ru/2.7/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat) позволяет определить очередь типа «первым вошёл – первым вышел» (FIFO) из *производителей*. Производителю нужен только один метод – `more()`, который должен возвращать данные для передачи по каналу. Производитель сигнализирует об исчерпании (*т.е.* об отсутствии данных), возвращая из метода `more()` пустую строку. После этого объект [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat) удаляет производителя из очереди и начинает использовать следующего производителя, если таковой имеется. Когда очередь производителей пуста, метод `handle_write()` ничего не делает. Для описания того, как распознавать конец или важную точку останова во входящей передаче от удалённой стороны, используется метод [`set_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.set_terminator) объекта канала.3233Чтобы создать работоспособный подкласс [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat), ваши входные методы [`collect_incoming_data()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.found_terminator) должны обрабатывать данные, которые канал получает асинхронно. Методы описаны ниже.3435#### `async_chat.close_when_done()`3637Помещает `None` в очередь производителей. Когда этот производитель извлекается из очереди, канал закрывается.3839#### `async_chat.collect_incoming_data(data)`4041Вызывается с параметром *data*, содержащим произвольный объём полученных данных. Метод по умолчанию (должен быть переопределён) возбуждает исключение [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError).4243#### `async_chat.discard_buffers()`4445В экстренных случаях этот метод удаляет любые данные, находящиеся во входных и/или выходных буферах, а также в очереди производителей.4647#### `async_chat.found_terminator()`4849Вызывается, когда входящий поток данных соответствует условию завершения, заданному [`set_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.set_terminator). Метод по умолчанию (должен быть переопределён) возбуждает исключение [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError). Буферизованные входные данные должны быть доступны через атрибут экземпляра.5051#### `async_chat.get_terminator()`5253Возвращает текущий терминатор канала.5455#### `async_chat.push(data)`5657Помещает данные в очередь канала для их передачи. Это всё, что нужно сделать, чтобы канал записал данные в сеть, хотя при желании можно использовать собственных производителей в более сложных схемах, например для реализации шифрования и разбиения на блоки.5859#### `async_chat.push_with_producer(producer)`6061Принимает объект производителя и добавляет его в очередь производителей, связанную с каналом. Когда все ранее помещённые производители будут исчерпаны, канал начнёт потреблять данные этого производителя, вызывая его метод `more()` и отправлять данные на удалённую сторону.6263#### `async_chat.set_terminator(term)`6465Устанавливает условие завершения, которое будет распознаваться на канале. `term` может быть одним из трёх типов значений, соответствующих трём различным способам обработки входящих протокольных данных.6667| терминатор | Описание |68| --- | --- |69| *строка* | Вызовет [`found_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.found_terminator), когда строка будет найдена во входном потоке данных |70| *целое* | Вызовет [`found_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.found_terminator), когда будет получено указанное количество символов |71| `None` | Канал продолжает собирать данные бесконечно |7273Обратите внимание, что любые данные после терминатора будут доступны для чтения каналом после вызова [`found_terminator()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat.found_terminator).7475## 17.7.1. asynchat – Вспомогательные классы7677#### `class asynchat.fifo([list=None])`7879[`fifo`](https://python-all.ru/2.7/library/asynchat.html#asynchat.fifo), содержащий данные, которые были помещены приложением, но ещё не извлечены для записи в канал. [`fifo`](https://python-all.ru/2.7/library/asynchat.html#asynchat.fifo) – это список, используемый для хранения данных и/или производителей до тех пор, пока они не понадобятся. Если передан аргумент *list*, он должен содержать производителей или элементы данных для записи в канал.8081#### `is_empty()`8283Возвращает `True` тогда и только тогда, когда очередь FIFO пуста.8485#### `first()`8687Возвращает элемент из очереди FIFO, который был [`push()`](https://python-all.ru/2.7/library/asynchat.html#asynchat.fifo.push) раньше всех остальных.8889#### `push(data)`9091Добавляет переданные данные (строку или объект-производитель) в producer fifo.9293#### `pop()`9495Если очередь FIFO не пуста, возвращает `True, first()`, удаляя извлечённый элемент. Для пустой очереди возвращает `False, None`.9697## 17.7.2. Пример asynchat9899Следующий неполный пример показывает, как HTTP-запросы можно читать с помощью [`async_chat`](https://python-all.ru/2.7/library/asynchat.html#asynchat.async_chat). Веб-сервер может создавать объект `http_request_handler` для каждого входящего подключения клиента. Обратите внимание, что изначально терминатор канала устанавливается на пустую строку в конце HTTP-заголовков, а флаг указывает, что заголовки читаются.100101После чтения заголовков, если запрос имеет тип POST (что указывает на наличие дополнительных данных во входном потоке), то заголовок `Content-Length:` используется для установки числового терминатора для чтения нужного количества данных из канала.102103Метод `handle_request()` вызывается после того, как все соответствующие входные данные были маршализированы, после установки терминатора канала на `None`, чтобы любые посторонние данные, отправленные веб-клиентом, игнорировались.104105```python106class http_request_handler(asynchat.async_chat):107108 def __init__(self, sock, addr, sessions, log):109 asynchat.async_chat.__init__(self, sock=sock)110 self.addr = addr111 self.sessions = sessions112 self.ibuffer = []113 self.obuffer = ""114 self.set_terminator("\r\n\r\n")115 self.reading_headers = True116 self.handling = False117 self.cgi_data = None118 self.log = log119120 def collect_incoming_data(self, data):121 """Буферизировать данные"""122 self.ibuffer.append(data)123124 def found_terminator(self):125 if self.reading_headers:126 self.reading_headers = False127 self.parse_headers("".join(self.ibuffer))128 self.ibuffer = []129 if self.op.upper() == "POST":130 clen = self.headers.getheader("content-length")131 self.set_terminator(int(clen))132 else:133 self.handling = True134 self.set_terminator(None)135 self.handle_request()136 elif not self.handling:137 self.set_terminator(None) # браузеры иногда отправляют лишние данные138 self.cgi_data = parse(self.headers, "".join(self.ibuffer))139 self.handling = True140 self.ibuffer = []141 self.handle_request()142```143