asynchat.md
1> **Источник:** https://python-all.ru/3.4/library/asynchat.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 18.7. [`asynchat`](https://python-all.ru/3.4/library/asynchat.html#module-asynchat) – Асинхронный обработчик команд/ответов через сокеты89**Исходный код:** [Lib/asynchat.py](https://python-all.ru/src/3.4/Lib/asynchat.py)1011---1213> **Примечание**14>15> Этот модуль существует только для обратной совместимости. Для нового кода рекомендуется использовать [`asyncio`](https://python-all.ru/3.4/library/asyncio.html#module-asyncio).1617Этот модуль построен на инфраструктуре [`asyncore`](https://python-all.ru/3.4/library/asyncore.html#module-asyncore), упрощая асинхронные клиенты и серверы и облегчая обработку протоколов, элементы которых завершаются произвольными строками или имеют переменную длину. [`asynchat`](https://python-all.ru/3.4/library/asynchat.html#module-asynchat) определяет абстрактный класс [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat), который вы наследуете, реализуя методы `collect_incoming_data()` и `found_terminator()`. Он использует тот же асинхронный цикл, что и [`asyncore`](https://python-all.ru/3.4/library/asyncore.html#module-asyncore), и два типа каналов – [`asyncore.dispatcher`](https://python-all.ru/3.4/library/asyncore.html#asyncore.dispatcher) и [`asynchat.async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat) – можно свободно смешивать в карте каналов. Обычно серверный канал [`asyncore.dispatcher`](https://python-all.ru/3.4/library/asyncore.html#asyncore.dispatcher) создаёт новые объекты каналов [`asynchat.async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat) при получении входящих запросов на подключение.1819#### `class asynchat.async_chat`2021Этот класс является абстрактным подклассом [`asyncore.dispatcher`](https://python-all.ru/3.4/library/asyncore.html#asyncore.dispatcher). Чтобы использовать код на практике, необходимо наследовать [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat), реализовав осмысленные методы [`collect_incoming_data()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.found_terminator). Методы [`asyncore.dispatcher`](https://python-all.ru/3.4/library/asyncore.html#asyncore.dispatcher) могут использоваться, хотя не все имеют смысл в контексте сообщений/ответов.2223Как и [`asyncore.dispatcher`](https://python-all.ru/3.4/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat) определяет набор событий, которые генерируются при анализе состояния сокета после вызова `select()`. После запуска цикла опроса методы объекта [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat) вызываются фреймворком обработки событий без участия программиста.2425Два атрибута класса можно изменять для повышения производительности или, возможно, даже для экономии памяти.2627#### `ac_in_buffer_size`2829Размер асинхронного входного буфера (по умолчанию `4096`).3031#### `ac_out_buffer_size`3233Размер асинхронного выходного буфера (по умолчанию `4096`).3435В отличие от [`asyncore.dispatcher`](https://python-all.ru/3.4/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat) позволяет определить очередь FIFO из *производителей*. У производителя должен быть только один метод – `more()`, который должен возвращать данные для передачи по каналу. Производитель сигнализирует об исчерпании (*т.е.* что у него больше нет данных), возвращая из своего метода `more()` пустой объект bytes. В этот момент объект [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat) удаляет производителя из fifo и начинает использовать следующего производителя, если такой есть. Когда очередь производителей пуста, метод `handle_write()` ничего не делает. Для описания того, как распознать конец или важную точку прерывания во входящей передаче от удаленной стороны, используется метод [`set_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.set_terminator) объекта канала.3637Чтобы создать работающий подкласс [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat), ваши методы ввода [`collect_incoming_data()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.found_terminator) должны обрабатывать данные, которые канал получает асинхронно. Методы описаны ниже.3839#### `async_chat.close_when_done()`4041Помещает `None` в очередь производителей. Когда этот производитель извлекается из очереди, канал закрывается.4243#### `async_chat.collect_incoming_data(data)`4445Вызывается с *data*, содержащим произвольный объем полученных данных. Метод по умолчанию, который должен быть переопределен, вызывает исключение [`NotImplementedError`](https://python-all.ru/3.4/library/exceptions.html#NotImplementedError).4647#### `async_chat.discard_buffers()`4849В экстренных случаях этот метод удаляет любые данные, находящиеся во входных и/или выходных буферах, а также в очереди производителей.5051#### `async_chat.found_terminator()`5253Вызывается, когда входящий поток данных соответствует условию завершения, установленному [`set_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.set_terminator). Метод по умолчанию, который должен быть переопределен, вызывает исключение [`NotImplementedError`](https://python-all.ru/3.4/library/exceptions.html#NotImplementedError). Буферизованные входные данные должны быть доступны через атрибут экземпляра.5455#### `async_chat.get_terminator()`5657Возвращает текущий терминатор канала.5859#### `async_chat.push(data)`6061Помещает данные в очередь канала для их передачи. Это всё, что нужно сделать, чтобы канал записал данные в сеть, хотя при желании можно использовать собственных производителей в более сложных схемах, например для реализации шифрования и разбиения на блоки.6263#### `async_chat.push_with_producer(producer)`6465Принимает объект производителя и добавляет его в очередь производителей, связанную с каналом. Когда все ранее помещенные производители будут исчерпаны, канал начнет потреблять данные этого производителя, вызывая его метод `more()`, и отправлять данные удаленной стороне.6667#### `async_chat.set_terminator(term)`6869Устанавливает условие завершения для распознавания на канале. `term` может быть значением одного из трех типов, соответствующих трем различным способам обработки входящих протокольных данных.7071| терминатор | Описание |72| --- | --- |73| *строка* | Будет вызывать [`found_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.found_terminator), когда строка будет найдена во входном потоке. |74| *целое* | Будет вызывать [`found_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.found_terminator), когда будет получено указанное количество символов. |75| `None` | Канал продолжает собирать данные бесконечно |7677Обратите внимание, что любые данные, следующие за терминатором, будут доступны для чтения каналом после вызова [`found_terminator()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat.found_terminator).7879## 18.7.1. asynchat – Вспомогательные классы8081#### `class asynchat.fifo(list=None)`8283A [`fifo`](https://python-all.ru/3.4/library/asynchat.html#asynchat.fifo) holding data which has been pushed by the application but not yet popped for writing to the channel. A [`fifo`](https://python-all.ru/3.4/library/asynchat.html#asynchat.fifo) is a list used to hold data and/or producers until they are required. If the *list* argument is provided then it should contain producers or data items to be written to the channel.8485#### `is_empty()`8687Возвращает `True`, если и только если fifo пуст.8889#### `first()`9091Возвращает самый старый элемент из fifo, добавленный с помощью [`push()`](https://python-all.ru/3.4/library/asynchat.html#asynchat.fifo.push).9293#### `push(data)`9495Добавляет переданные данные (строку или объект-производитель) в producer fifo.9697#### `pop()`9899Если fifo не пуст, возвращает `True, first()`, удаляя извлечённый элемент. Если fifo пуст, возвращает `False, None`.100101## 18.7.2. Пример asynchat102103Следующий неполный пример показывает, как можно читать HTTP-запросы с помощью [`async_chat`](https://python-all.ru/3.4/library/asynchat.html#asynchat.async_chat). Веб-сервер может создавать объект `http_request_handler` для каждого входящего клиентского соединения. Обратите внимание, что изначально терминатор канала устанавливается таким образом, чтобы соответствовать пустой строке в конце HTTP-заголовков, и флаг указывает, что заголовки читаются.104105После чтения заголовков, если запрос имеет тип POST (что указывает на наличие дополнительных данных во входном потоке), то заголовок `Content-Length:` используется для установки числового терминатора, чтобы прочитать нужное количество данных из канала.106107Метод `handle_request()` вызывается после того, как все соответствующие входные данные были маршалированы, после установки терминатора канала в `None`, чтобы любые посторонние данные, отправленные веб-клиентом, игнорировались.108109```python110import asynchat111112class http_request_handler(asynchat.async_chat):113114 def __init__(self, sock, addr, sessions, log):115 asynchat.async_chat.__init__(self, sock=sock)116 self.addr = addr117 self.sessions = sessions118 self.ibuffer = []119 self.obuffer = b""120 self.set_terminator(b"\r\n\r\n")121 self.reading_headers = True122 self.handling = False123 self.cgi_data = None124 self.log = log125126 def collect_incoming_data(self, data):127 """Буферизировать данные"""128 self.ibuffer.append(data)129130 def found_terminator(self):131 if self.reading_headers:132 self.reading_headers = False133 self.parse_headers(b"".join(self.ibuffer))134 self.ibuffer = []135 if self.op.upper() == b"POST":136 clen = self.headers.getheader("content-length")137 self.set_terminator(int(clen))138 else:139 self.handling = True140 self.set_terminator(None)141 self.handle_request()142 elif not self.handling:143 self.set_terminator(None) # браузеры иногда отправляют лишние данные144 self.cgi_data = parse(self.headers, b"".join(self.ibuffer))145 self.handling = True146 self.ibuffer = []147 self.handle_request()148```149