Содержание страницы
17.6. asynchat – Асинхронный обработчик команд/ответов через сокет¶asynchat – Asynchronous socket command/response handler
Этот модуль основывается на инфраструктуре 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.6.1. asynchat – Вспомогательные классы¶asynchat - Auxiliary Classes
- class asynchat.fifo(list=None)¶
A fifo holding data which has been pushed by the application but not yet popped for writing to the channel. A fifo is a list used to hold data and/or producers until they are required. If the list argument is provided then it should contain producers or data items to be written to the channel.
- is_empty()¶
- Возвращает True, если и только если fifo пуст.
- push(data)¶
- Добавляет переданные данные (строку или объект-производитель) в producer fifo.
- pop()¶
- Если fifo не пуст, возвращает True, first(), удаляя извлечённый элемент. Если fifo пуст, возвращает False, None.
17.6.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 = 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("".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()