asynchat — Асинхронный сокет обработчика запрос/ответ¶
Source code: 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()пустого байтового объекта. В этот момент объект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)¶ Вызывается с данными, содержащими произвольное количество полученных данных. Метод по умолчанию, который должен быть переопределен, вызывающий исключение
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может быть любым из трех типов значений, соответствующим тремя различными способами обработки протокола входящих данных .term Описание string Вызывает found_terminator()при обнаружении строки во входном потокеinteger Вызов found_terminator()после получения указанного количества символовNoneКанал продолжает собирать данные всегда Следует отметить, что любые данные, следующие за терминатором, будут доступны для считывания каналом после вызова
found_terminator().
Пример asynchat¶
В следующем частичном примере показано, как читать HTTP запросы с помощью async_chat.
Веб-сервер может создать объект http_request_handler для каждого входящего
клиентского подключения. Обратите внимание, что первоначально признак конца канала должен
соответствовать пустой строке в конеце заголовков HTTP и флага указателя того,
что заголовки нужно начать читать.
После считывания заголовков, если запрос типа POST (с указанием что
дополнительные данные присутствуют во входном потоке), то используется
заголовок Content-Length: для установки числового терминатора для чтения
из канала правильного объёма данных.
Метод: meth: 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):
"""Buffer the 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) # browsers sometimes over-send
self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()
