asynchat
— Обработчик команд/ответов асинхронного сокета¶
Исходный код: Lib/asynchat.py.
Не рекомендуется, начиная с версии 3.6: asynchat
будет удален в Python 3.12 (подробнее см. PEP 594). Пожалуйста, используйте 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()
объекта channel, чтобы описать, как распознать конец или важную точку останова во входящей передаче от удаленной конечной точки.Чтобы построить функционирующий подкласс
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 Пример¶
Следующий частичный пример показывает, как 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):
"""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()