asynchat — Асинхронный обработчик команд/ответов сокета
Исходный код: 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
Описание
строка
Вызовет
found_terminator(), когда строка будет найдена в потоке вводацелое число
Вызовет
found_terminator(), когда указанное количество символов будет полученоNoneКанал продолжает собирать данные вечно
Обратите внимание, что любые данные после терминатора будут доступны для чтения каналом после вызова
found_terminator().
18.7.1. Пример 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()
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.6/library/asynchat.html