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

asynchat – Асинхронный обработчик команд/ответов через сокетыasynchat – Asynchronous socket command/response handler

Исходный код: Lib/asynchat.py

Устарело с версии 3.6, будет удалено в версии 3.12: Модуль asynchat устарел (см. PEP 594 подробнее). Вместо него используйте asyncio.


Примечание

Этот модуль существует только для обратной совместимости. Для нового кода рекомендуется использовать asyncio.

Этот модуль построен на основе инфраструктуры asyncore, упрощая реализацию асинхронных клиентов и серверов, а также обработку протоколов, элементы которых завершаются произвольными строками или имеют переменную длину. asynchat определяет абстрактный класс async_chat, который нужно наследовать, реализуя методы collect_incoming_data() и found_terminator(). Он использует тот же асинхронный цикл, что и asyncore, и два типа каналов – asyncore.dispatcher и asynchat.async_chat – могут свободно сочетаться в карте каналов. Обычно серверный канал asyncore.dispatcher создаёт новые объекты канала asynchat.async_chat при получении входящих запросов на соединение.

Доступность: not Emscripten, not WASI.

Этот модуль не работает или недоступен на платформах WebAssembly wasm32-emscripten и wasm32-wasi. См. платформы WebAssembly для получения дополнительной информации.

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(), чтобы задать способ распознавания конца или важной точки останова во входящей передаче от удалённой стороны.

Чтобы создать работоспособный подкласс 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 может быть одним из трёх типов значений, соответствующих трём различным способам обработки входящих протокольных данных.

term

Описание

строка

Вызовет found_terminator(), когда строка будет найдена во входном потоке данных

целое

Вызовет found_terminator(), когда будет получено указанное количество символов

None

Канал продолжает собирать данные бесконечно

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

Пример asynchatasynchat Example

Следующий неполный пример показывает, как 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):
        """Буферизировать данные"""
        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)  # браузеры иногда отправляют лишние данные
            self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
            self.handling = True
            self.ibuffer = []
            self.handle_request()