Документация Python неофициальный перевод
Содержание страницы

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 удаляет производителя из fifo и начинает использовать следующего, если таковой имеется. Когда очередь производителей пуста, метод 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._collect_incoming_data(data)
Пример реализации процедуры сбора данных для использования вместе с _get_data() в задаваемой пользователем found_terminator().
async_chat.discard_buffers()
В экстренных случаях этот метод удаляет любые данные, находящиеся во входных и/или выходных буферах, а также в очереди производителей.
async_chat.found_terminator()
Вызывается, когда входящий поток данных соответствует условию завершения, установленному set_terminator(). Метод по умолчанию, который должен быть переопределен, вызывает исключение NotImplementedError. Буферизованные входные данные должны быть доступны через атрибут экземпляра.
async_chat._get_data()
Возвращает и очищает данные, полученные с помощью примера реализации _collect_incoming_data().
async_chat.get_terminator()
Возвращает текущий терминатор канала.
async_chat.handle_close()
Вызывается, когда канал закрыт. Метод по умолчанию молча закрывает сокет канала.
async_chat.handle_read()
Вызывается, когда в асинхронном цикле происходит событие чтения на сокете канала. Метод по умолчанию проверяет условие завершения, установленное set_terminator(), которое может быть либо появлением определённой строки во входном потоке, либо получением определённого количества символов. Когда терминатор найден, handle_read() вызывает метод found_terminator() после вызова collect_incoming_data() с данными, предшествующими завершающему условию.
async_chat.handle_write()
Вызывается, когда приложение может записать данные в канал. Метод по умолчанию вызывает метод initiate_send(), который, в свою очередь, вызывает refill_buffer() для сбора данных из очереди производителей, связанной с каналом.
async_chat.push(data)
Создаёт объект simple_producer (см. ниже), содержащий данные, и помещает его в очередь канала producer_fifo для обеспечения передачи. Это всё, что нужно сделать, чтобы канал записал данные в сеть, хотя можно использовать собственных производителей в более сложных схемах, например, для шифрования или разбиения на части.
async_chat.push_with_producer(producer)
Принимает объект производителя и добавляет его в очередь производителей, связанную с каналом. Когда все ранее помещенные производители будут исчерпаны, канал начнет потреблять данные этого производителя, вызывая его метод more(), и отправлять данные удаленной стороне.
async_chat.readable()
Должен возвращать True, чтобы канал был включён в набор каналов, проверяемых циклом select на читаемость.
async_chat.refill_buffer()
Пополняет выходной буфер, вызывая метод more() производителя в начале fifo. Если он истощён, производитель извлекается из fifo и активируется следующий. Если текущий производитель равен или становится None, канал закрывается.
async_chat.set_terminator(term)

Устанавливает условие завершения для распознавания на канале. term может быть значением одного из трех типов, соответствующих трем различным способам обработки входящих протокольных данных.

терминатор Описание
строка Будет вызывать found_terminator(), когда строка будет найдена во входном потоке.
целое Будет вызывать found_terminator(), когда будет получено указанное количество символов.
None Канал продолжает собирать данные бесконечно

Обратите внимание, что любые данные, следующие за терминатором, будут доступны для чтения каналом после вызова found_terminator().

async_chat.writable()
Должен возвращать True, пока элементы остаются в очереди производителя (fifo) или канал подключен и его выходной буфер не пуст.

asynchat – вспомогательные классы и функцииasynchat - Auxiliary Classes and Functions

class asynchat.simple_producer(data[, buffer_size=512])

simple_producer принимает кусок данных и необязательный размер буфера. Повторные вызовы его метода more() возвращают последовательные куски данных, размер которых не превышает buffer_size.

more()
Выдаёт следующий кусок информации от производителя или возвращает пустую строку.
class asynchat.fifo([list=None])

Каждый канал поддерживает fifo, содержащую данные, которые были помещены приложением, но ещё не извлечены для записи в канал. fifo представляет собой список, используемый для хранения данных и/или производителей до тех пор, пока они не потребуются. Если аргумент list передан, он должен содержать производителей или элементы данных для записи в канал.

is_empty()
Возвращает True, если и только если fifo пуст.
first()
Возвращает самый старый элемент из fifo, добавленный с помощью push().
push(data)
Добавляет переданные данные (строку или объект-производитель) в producer fifo.
pop()
Если fifo не пуст, возвращает True, first(), удаляя извлечённый элемент. Если fifo пуст, возвращает False, None.

Модуль asynchat также определяет одну служебную функцию, которая может быть полезна при работе с сетью и анализе текста.

asynchat.find_prefix_at_end(haystack, needle)
Возвращает True, если строка haystack заканчивается любым непустым префиксом строки needle.

Пример asynchatasynchat 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):
        """Buffer the 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) # browsers sometimes over-send
            self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
            self.handling = True
            self.ibuffer = []
            self.handle_request()