Содержание страницы
asynchat – Асинхронный обработчик команд/ответов через сокеты¶asynchat – Asynchronous socket command/response handler
Исходный код: Lib/asynchat.py
Устарело с версии 3.6: Вместо этого используйте asyncio.
Примечание
Этот модуль существует только для обратной совместимости. Для нового кода
рекомендуется использовать asyncio.
Этот модуль построен на основе инфраструктуры asyncore, упрощая
реализацию асинхронных клиентов и серверов, а также обработку протоколов,
элементы которых завершаются произвольными строками или имеют переменную длину.
asynchat определяет абстрактный класс async_chat, который нужно
наследовать, реализуя методы collect_incoming_data() и
found_terminator(). Он использует тот же асинхронный цикл, что и
asyncore, и два типа каналов – asyncore.dispatcher
и asynchat.async_chat – могут свободно сочетаться в карте каналов.
Обычно серверный канал asyncore.dispatcher создаёт новые
объекты канала asynchat.async_chat при получении входящих
запросов на соединение.
-
class
asynchat.async_chat¶ Этот класс является абстрактным подклассом
asyncore.dispatcher. Чтобы использовать его на практике, необходимо наследоватьasync_chat, реализовав осмысленные методыcollect_incoming_data()иfound_terminator(). Методыasyncore.dispatcherможно использовать, хотя не все из них имеют смысл в контексте сообщений/ответов.Как и
asyncore.dispatcher,async_chatопределяет набор событий, порождаемых анализом состояния сокета после вызоваselect(). Как только цикл опроса запущен, методы объектаasync_chatвызываются каркасом обработки событий без каких-либо действий со стороны программиста.Два атрибута класса можно изменять для повышения производительности или, возможно, даже для экономии памяти.
-
ac_in_buffer_size¶ Размер асинхронного входного буфера (по умолчанию
4096).
-
ac_out_buffer_size¶ Размер асинхронного выходного буфера (по умолчанию
4096).
В отличие от
asyncore.dispatcher,async_chatпозволяет определить очередь FIFO из производителей. Производителю нужен всего один методmore(), который должен возвращать данные для передачи по каналу. Производитель сигнализирует об исчерпании данных (т.е. что у него больше нет данных), возвращая из методаmore()пустой объект bytes. В этот момент объектasync_chatудаляет производителя из очереди и начинает использовать следующего, если он есть. Когда очередь производителей пуста, методhandle_write()ничего не делает. Используйте метод каналаset_terminator(), чтобы задать способ распознавания конца или важной точки останова во входящей передаче от удалённой стороны.Чтобы создать работоспособный подкласс
async_chat, ваши входные методыcollect_incoming_data()иfound_terminator()должны обрабатывать данные, которые канал получает асинхронно. Методы описаны ниже.-
-
async_chat.close_when_done()¶ Помещает
Noneв очередь производителей. Когда этот производитель извлекается из очереди, он вызывает закрытие канала.
-
async_chat.collect_incoming_data(data)¶ Вызывается с параметром data, содержащим произвольный объём полученных данных. Метод по умолчанию (должен быть переопределён) возбуждает исключение
NotImplementedError.
-
async_chat.discard_buffers()¶ В аварийных ситуациях этот метод сбрасывает все данные, находящиеся во входном и/или выходном буферах, а также в очереди производителей.
-
async_chat.found_terminator()¶ Вызывается, когда входящий поток данных соответствует условию завершения, заданному
set_terminator(). Метод по умолчанию (должен быть переопределён) возбуждает исключениеNotImplementedError. Буферизованные входные данные должны быть доступны через атрибут экземпляра.
-
async_chat.get_terminator()¶ Возвращает текущий терминатор канала.
-
async_chat.push(data)¶ Помещает данные в очередь канала для гарантированной передачи. Это всё, что нужно сделать, чтобы канал записал данные в сеть, хотя в более сложных схемах можно использовать собственных производителей, например, для шифрования или разбиения на блоки.
-
async_chat.push_with_producer(producer)¶ Принимает объект-производитель и добавляет его в очередь производителей, связанную с каналом. Когда все текущие добавленные производители будут исчерпаны, канал начнёт потреблять данные этого производителя, вызывая его метод
more(), и отправлять данные на удалённую конечную точку.
-
async_chat.set_terminator(term)¶ Устанавливает условие завершения, которое будет распознаваться на канале.
termможет быть одним из трёх типов значений, соответствующих трём различным способам обработки входящих протокольных данных.терминатор
Описание
строка
Вызовет
found_terminator(), когда строка будет найдена во входном потоке данныхцелое
Вызовет
found_terminator(), когда будет получено указанное количество символовNoneКанал продолжает собирать данные бесконечно
Обратите внимание, что любые данные после терминатора будут доступны для чтения каналом после вызова
found_terminator().
Пример asynchat¶asynchat Example
Следующий неполный пример показывает, как HTTP-запросы можно читать с помощью
async_chat. Веб-сервер может создавать объект
http_request_handler для каждого входящего подключения клиента.
Обратите внимание, что изначально терминатор канала устанавливается на пустую строку в
конце HTTP-заголовков, а флаг указывает, что заголовки читаются.
После чтения заголовков, если запрос имеет тип POST (что указывает
на наличие дополнительных данных во входном потоке), то заголовок
Content-Length: используется для установки числового терминатора для чтения
нужного количества данных из канала.
Метод handle_request() вызывается после того, как все соответствующие входные данные
были маршализированы, после установки терминатора канала на None, чтобы
любые посторонние данные, отправленные веб-клиентом, игнорировались.
import asynchat
class http_request_handler(asynchat.async_chat):
def __init__(self, sock, addr, sessions, log):
asynchat.async_chat.__init__(self, sock=sock)
self.addr = addr
self.sessions = sessions
self.ibuffer = []
self.obuffer = b""
self.set_terminator(b"\r\n\r\n")
self.reading_headers = True
self.handling = False
self.cgi_data = None
self.log = log
def collect_incoming_data(self, data):
"""Буферизировать данные"""
self.ibuffer.append(data)
def found_terminator(self):
if self.reading_headers:
self.reading_headers = False
self.parse_headers(b"".join(self.ibuffer))
self.ibuffer = []
if self.op.upper() == b"POST":
clen = self.headers.getheader("content-length")
self.set_terminator(int(clen))
else:
self.handling = True
self.set_terminator(None)
self.handle_request()
elif not self.handling:
self.set_terminator(None) # браузеры иногда отправляют лишние данные
self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()