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()