asynchat
— обробник команд/відповідей асинхронного сокета¶
Вихідний код: Lib/asynchat.py
Застаріло починаючи з версії 3.6: Please use asyncio
instead.
Примітка
Цей модуль існує лише для зворотної сумісності. Для нового коду ми рекомендуємо використовувати 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()
, який повертає порожній об’єкт 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)¶ Takes a producer object and adds it to the producer queue associated with the channel. When all currently-pushed producers have been exhausted the channel will consume this producer’s data by calling its
more()
method and send the data to the remote endpoint.
-
async_chat.
set_terminator
(term)¶ Встановлює умову завершення, яку буде розпізнано на каналі.
term
може бути будь-яким із трьох типів значення, що відповідає трьом різним способам обробки вхідних даних протоколу.термін
опис
рядок
Викличе
found_terminator()
, коли рядок знайдено у вхідному потоціціле
Викличе
found_terminator()
, коли буде отримано вказану кількість символівЖодного
Канал продовжує збирати дані вічно
Зауважте, що будь-які дані після термінатора будуть доступні для читання каналом після виклику
found_terminator()
.
Приклад асинчат¶
Наступний частковий приклад показує, як 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()