asynchat.md
1> **Источник:** https://python-all.ru/2.6/library/asynchat.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.7. `asynchat` – Асинхронный обработчик команд/ответов через сокеты89Этот модуль основывается на инфраструктуре [`asyncore`](https://python-all.ru/2.6/library/asyncore.html#module-asyncore), упрощая асинхронные клиенты и серверы и облегчая работу с протоколами, элементы которых завершаются произвольными строками или имеют переменную длину. `asynchat` определяет абстрактный класс [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat), который вы наследуете, реализуя методы `collect_incoming_data()` и `found_terminator()`. Он использует тот же асинхронный цикл, что и [`asyncore`](https://python-all.ru/2.6/library/asyncore.html#module-asyncore), и два типа каналов, [`asyncore.dispatcher`](https://python-all.ru/2.6/library/asyncore.html#asyncore.dispatcher) и [`asynchat.async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat), можно свободно смешивать в карте каналов. Обычно серверный канал [`asyncore.dispatcher`](https://python-all.ru/2.6/library/asyncore.html#asyncore.dispatcher) создаёт новые объекты канала [`asynchat.async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat) при получении входящих запросов на соединение.1011#### `class asynchat.async_chat`1213Этот класс является абстрактным подклассом [`asyncore.dispatcher`](https://python-all.ru/2.6/library/asyncore.html#asyncore.dispatcher). Чтобы использовать код на практике, необходимо наследовать [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat), реализовав осмысленные методы [`collect_incoming_data()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.found_terminator). Методы [`asyncore.dispatcher`](https://python-all.ru/2.6/library/asyncore.html#asyncore.dispatcher) могут использоваться, хотя не все имеют смысл в контексте сообщений/ответов.1415Как и [`asyncore.dispatcher`](https://python-all.ru/2.6/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat) определяет набор событий, которые генерируются при анализе состояния сокета после вызова `select()`. После запуска цикла опроса методы объекта [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat) вызываются фреймворком обработки событий без участия программиста.1617Два атрибута класса можно изменять для повышения производительности или, возможно, даже для экономии памяти.1819#### `ac_in_buffer_size`2021Размер асинхронного входного буфера (по умолчанию2223`4096`2425).2627#### `ac_out_buffer_size`2829Размер асинхронного выходного буфера (по умолчанию3031`4096`3233).3435В отличие от [`asyncore.dispatcher`](https://python-all.ru/2.6/library/asyncore.html#asyncore.dispatcher), [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat) позволяет определить очередь по принципу «первым вошёл – первым вышел» (FIFO) из *производителей*. Производителю нужен лишь один метод, `more()`, который должен возвращать данные для передачи по каналу. Производитель сигнализирует об исчерпании (*т.е.* что у него больше нет данных), когда его метод `more()` возвращает пустую строку. На этом этапе объект [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat) удаляет производителя из очереди и начинает использовать следующего производителя, если он есть. Когда очередь производителей пуста, метод `handle_write()` ничего не делает. Метод канала [`set_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.set_terminator) используется для описания того, как распознавать конец или важную точку прерывания во входящей передаче от удалённой стороны.3637Чтобы создать работающий подкласс [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat), ваши методы ввода [`collect_incoming_data()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.collect_incoming_data) и [`found_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.found_terminator) должны обрабатывать данные, которые канал получает асинхронно. Методы описаны ниже.3839#### `async_chat.close_when_done()`4041Помещает4243`None`4445в очередь производителей. Когда этот производитель извлекается из очереди, канал закрывается.4647#### `async_chat.collect_incoming_data(data)`4849Вызывается с5051*data*5253, содержащим произвольный объем полученных данных. Метод по умолчанию, который должен быть переопределен, вызывает исключение5455[`NotImplementedError`](https://python-all.ru/2.6/library/exceptions.html#exceptions.NotImplementedError)5657.5859#### `async_chat.discard_buffers()`6061В экстренных случаях этот метод удаляет любые данные, находящиеся во входных и/или выходных буферах, а также в очереди производителей.6263#### `async_chat.found_terminator()`6465Вызывается, когда входящий поток данных соответствует условию завершения, установленному6667[`set_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.set_terminator)6869. Метод по умолчанию, который должен быть переопределен, вызывает исключение7071[`NotImplementedError`](https://python-all.ru/2.6/library/exceptions.html#exceptions.NotImplementedError)7273. Буферизованные входные данные должны быть доступны через атрибут экземпляра.7475#### `async_chat.get_terminator()`7677Возвращает текущий терминатор канала.7879#### `async_chat.push(data)`8081Помещает данные в очередь канала для их передачи. Это всё, что нужно сделать, чтобы канал записал данные в сеть, хотя при желании можно использовать собственных производителей в более сложных схемах, например для реализации шифрования и разбиения на блоки.8283#### `async_chat.push_with_producer(producer)`8485Принимает объект производителя и добавляет его в очередь производителей, связанную с каналом. Когда все ранее помещенные производители будут исчерпаны, канал начнет потреблять данные этого производителя, вызывая его метод8687`more()`8889, и отправлять данные удаленной стороне.9091#### `async_chat.set_terminator(term)`9293Устанавливает условие завершения для распознавания на канале. `term` может быть значением одного из трех типов, соответствующих трем различным способам обработки входящих протокольных данных.9495| терминатор | Описание |96| --- | --- |97| *строка* | Будет вызывать [`found_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.found_terminator), когда строка будет найдена во входном потоке. |98| *целое* | Будет вызывать [`found_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.found_terminator), когда будет получено указанное количество символов. |99| `None` | Канал продолжает собирать данные бесконечно |100101Обратите внимание, что любые данные, следующие за терминатором, будут доступны для чтения каналом после вызова [`found_terminator()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat.found_terminator).102103## 17.7.1. asynchat – Вспомогательные классы104105#### `class asynchat.fifo([list=None])`106107A [`fifo`](https://python-all.ru/2.6/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/2.6/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.108109#### `is_empty()`110111Возвращает112113`True`114115, если и только если fifo пуст.116117#### `first()`118119Возвращает самый старый элемент из fifo, добавленный с помощью120121[`push()`](https://python-all.ru/2.6/library/asynchat.html#asynchat.fifo.push)122123.124125#### `push(data)`126127Добавляет переданные данные (строку или объект-производитель) в producer fifo.128129#### `pop()`130131Если fifo не пуст, возвращает132133`True, first()`134135, удаляя извлечённый элемент. Если fifo пуст, возвращает136137`False, None`138139.140141## 17.7.2. Пример asynchat142143Следующий неполный пример показывает, как можно читать HTTP-запросы с помощью [`async_chat`](https://python-all.ru/2.6/library/asynchat.html#asynchat.async_chat). Веб-сервер может создавать объект `http_request_handler` для каждого входящего клиентского соединения. Обратите внимание, что изначально терминатор канала устанавливается таким образом, чтобы соответствовать пустой строке в конце HTTP-заголовков, и флаг указывает, что заголовки читаются.144145После чтения заголовков, если запрос имеет тип POST (что указывает на наличие дополнительных данных во входном потоке), то заголовок `Content-Length:` используется для установки числового терминатора, чтобы прочитать нужное количество данных из канала.146147Метод `handle_request()` вызывается после того, как все соответствующие входные данные были маршалированы, после установки терминатора канала в `None`, чтобы любые посторонние данные, отправленные веб-клиентом, игнорировались.148149```python150class http_request_handler(asynchat.async_chat):151152 def __init__(self, sock, addr, sessions, log):153 asynchat.async_chat.__init__(self, sock=sock)154 self.addr = addr155 self.sessions = sessions156 self.ibuffer = []157 self.obuffer = ""158 self.set_terminator("\r\n\r\n")159 self.reading_headers = True160 self.handling = False161 self.cgi_data = None162 self.log = log163164 def collect_incoming_data(self, data):165 """Буферизировать данные"""166 self.ibuffer.append(data)167168 def found_terminator(self):169 if self.reading_headers:170 self.reading_headers = False171 self.parse_headers("".join(self.ibuffer))172 self.ibuffer = []173 if self.op.upper() == "POST":174 clen = self.headers.getheader("content-length")175 self.set_terminator(int(clen))176 else:177 self.handling = True178 self.set_terminator(None)179 self.handle_request()180 elif not self.handling:181 self.set_terminator(None) # браузеры иногда отправляют лишние данные182 self.cgi_data = parse(self.headers, "".join(self.ibuffer))183 self.handling = True184 self.ibuffer = []185 self.handle_request()186```187