asynchat
— Обработчик команд/ответов асинхронного сокета
Не рекомендуется, начиная с версии 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 из producers. У производителя должен быть только один метод,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) Вызывается с 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:
используется для установки числового признака
конца для чтения нужного количества данных из канала.
Метод 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()