asynchat
— обробник команд/відповідей асинхронного сокета¶
Вихідний код: Lib/asynchat.py
Застаріло починаючи з версії 3.6: asynchat
will be removed in Python 3.12
(see PEP 594 for details).
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()