Содержание страницы
17.7. asynchat – Асинхронный обработчик команд/ответов через сокеты¶asynchat – Asynchronous socket command/response handler
Исходный код: Lib/asynchat.py
Этот модуль построен на основе инфраструктуры 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)¶ Вызывается с параметром 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().
17.7.1. asynchat – Вспомогательные классы¶asynchat - Auxiliary Classes
-
class
asynchat.fifo([list=None])¶ fifo, содержащий данные, которые были помещены приложением, но ещё не извлечены для записи в канал.fifo– это список, используемый для хранения данных и/или производителей до тех пор, пока они не понадобятся. Если передан аргумент list, он должен содержать производителей или элементы данных для записи в канал.-
is_empty()¶ Возвращает
Trueтогда и только тогда, когда очередь FIFO пуста.
-
push(data)¶ Добавляет переданные данные (строку или объект-производитель) в producer fifo.
-
pop()¶ Если очередь FIFO не пуста, возвращает
True, first(), удаляя извлечённый элемент. Для пустой очереди возвращаетFalse, None.
-
17.7.2. Пример asynchat¶asynchat Example
Следующий неполный пример показывает, как HTTP-запросы можно читать с помощью
async_chat. Веб-сервер может создавать объект
http_request_handler для каждого входящего подключения клиента.
Обратите внимание, что изначально терминатор канала устанавливается на пустую строку в
конце HTTP-заголовков, а флаг указывает, что заголовки читаются.
После чтения заголовков, если запрос имеет тип POST (что указывает
на наличие дополнительных данных во входном потоке), то заголовок
Content-Length: используется для установки числового терминатора для чтения
нужного количества данных из канала.
Метод handle_request() вызывается после того, как все соответствующие входные данные
были маршализированы, после установки терминатора канала на None, чтобы
любые посторонние данные, отправленные веб-клиентом, игнорировались.
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 = ""
self.set_terminator("\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("".join(self.ibuffer))
self.ibuffer = []
if self.op.upper() == "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, "".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()