asyncore
— обробник асинхронного сокета¶
Вихідний код: Lib/asyncore.py
Застаріло з версії 3.6, буде видалено у версії 3.12: The asyncore
module is deprecated
(see PEP 594 for details).
Please use asyncio
instead.
Примітка
Цей модуль існує лише для зворотної сумісності. Для нового коду ми рекомендуємо використовувати asyncio
.
Цей модуль надає базову інфраструктуру для написання клієнтів і серверів служби асинхронних сокетів.
Availability: not Emscripten, not WASI.
This module does not work or is not available on WebAssembly platforms
wasm32-emscripten
and wasm32-wasi
. See
WebAssembly platforms for more information.
Є лише два способи змусити програму на одному процесорі виконувати «більш ніж одну дію одночасно». Багатопотокове програмування є найпростішим і найпопулярнішим способом зробити це, але існує ще одна зовсім інша техніка, яка дає вам змогу отримати майже всі переваги багатопоточності без фактичного використання кількох потоків. Це дійсно практично, лише якщо ваша програма значною мірою пов’язана з вводом-виводом. Якщо ваша програма пов’язана з процесором, то вам справді потрібні потоки з випереджальним плануванням. Проте мережеві сервери рідко пов’язані з процесором.
Якщо ваша операційна система підтримує системний виклик select()
у своїй бібліотеці вводу-виводу (і майже всі так підтримує), ви можете використовувати її для жонглювання кількома каналами зв’язку одночасно; виконувати іншу роботу, поки ваш ввід/вивід виконується у «фоновому режимі». Хоча ця стратегія може здатися дивною та складною, особливо спочатку, її багато в чому легше зрозуміти та контролювати, ніж багатопотокове програмування. Модуль asyncore
вирішує багато складних проблем, роблячи завдання побудови складних високопродуктивних мережевих серверів і клієнтів миттєвим. Для «розмовних» програм і протоколів супутній модуль asynchat
є безцінним.
Основна ідея обох модулів полягає у створенні одного або кількох мережевих каналів, екземплярів класу asyncore.dispatcher
і asynchat.async_chat
. Створення каналів додає їх до глобальної карти, яка використовується функцією loop()
, якщо ви не надаєте їй свою власну карту.
Після створення початкового(их) каналу(ів) виклик функції loop()
активує службу каналу, яка продовжується, доки не буде закрито останній канал (включаючи будь-який, який було додано до карти під час асинхронної служби).
- asyncore.loop([timeout[, use_poll[, map[, count]]]])¶
Введіть цикл опитування, який завершується після проходження підрахунку або закриття всіх відкритих каналів. Усі аргументи необов’язкові. Параметр count за замовчуванням має значення
None
, що призводить до завершення циклу лише після закриття всіх каналів. Аргумент timeout встановлює параметр часу очікування для відповідного викликуselect()
абоpoll()
, виміряний у секундах; за замовчуванням 30 секунд. Параметр use_poll, якщо він істинний, вказує, щоpoll()
слід використовувати замістьselect()
(за замовчуваннямFalse
).Параметр map — це словник, елементи якого є каналами для перегляду. Коли канали закриваються, вони видаляються з карти. Якщо map опущено, використовується глобальна карта. Канали (екземпляри
asyncore.dispatcher
,asynchat.async_chat
та їхні підкласи) можна вільно змішувати на карті.
- class asyncore.dispatcher¶
Клас
dispatcher
— це тонка обгортка навколо об’єкта сокета низького рівня. Щоб зробити його більш корисним, він має кілька методів для обробки подій, які викликаються з асинхронного циклу. В іншому випадку його можна розглядати як звичайний неблокуючий об’єкт сокета.Запуск подій низького рівня в певний час або в певних станах з’єднання повідомляє асинхронному циклу, що відбулися певні події вищого рівня. Наприклад, якщо ми запросили сокет для з’єднання з іншим хостом, ми знаємо, що з’єднання було встановлено, коли сокет стане доступним для запису вперше (на цьому етапі ви знаєте, що можете писати в нього з очікуванням успіху ). Передбачувані події вищого рівня:
Подія
опис
handle_connect()
Передбачено першою подією читання або запису
handle_close()
Означається подією читання без доступних даних
handle_accepted()
Означається подією читання в сокеті, що прослуховує
Під час асинхронної обробки методи кожного зіставленого каналу
readable()
іwritable()
використовуються для визначення того, чи слід додати сокет каналу до списку каналівselect()
ed абоpoll()
для подій читання та запису.Таким чином, набір подій каналу більший, ніж подій основного сокета. Нижче наведено повний набір методів, які можна перевизначати у вашому підкласі:
- handle_read()¶
Викликається, коли асинхронний цикл виявляє, що виклик
read()
для сокета каналу буде успішним.
- handle_write()¶
Викликається, коли асинхронний цикл виявляє, що доступний для запису сокет можна записати. Часто цей метод реалізує необхідну буферизацію для продуктивності. Наприклад:
def handle_write(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:]
- handle_expt()¶
Викликається, коли для підключення до сокета є позасмугові (OOB) дані. Цього майже ніколи не станеться, оскільки OOB слабко підтримується і рідко використовується.
- handle_connect()¶
Викликається, коли активний сокет відкривача фактично встановлює з’єднання. Наприклад, може надіслати банер «вітання» або ініціювати узгодження протоколу з віддаленою кінцевою точкою.
- handle_close()¶
Викликається, коли сокет закрито.
- handle_error()¶
Викликається, коли виникає виняток і не обробляється інакше. Версія за замовчуванням друкує скорочену трасування.
- handle_accept()¶
Викликається на каналах прослуховування (пасивні відкривачі), коли можна встановити з’єднання з новою віддаленою кінцевою точкою, яка випустила виклик
connect()
для локальної кінцевої точки. Застаріло у версії 3.2; замість цього використовуйтеhandle_accepted()
.Застаріло починаючи з версії 3.2.
- handle_accepted(sock, addr)¶
Викликається на каналах прослуховування (пасивні відкривачі), коли встановлено з’єднання з новою віддаленою кінцевою точкою, яка випустила виклик
connect()
для локальної кінцевої точки. sock — це новий об’єкт сокета, який можна використовувати для надсилання та отримання даних у з’єднанні, а addr — це адреса, прив’язана до сокета на іншому кінці з’єднання.Нове в версії 3.2.
- readable()¶
Викликається кожного разу в асинхронному циклі, щоб визначити, чи слід додавати сокет каналу до списку, у якому можуть відбуватися події читання. Метод за замовчуванням просто повертає
True
, вказуючи, що за замовчуванням усі канали будуть зацікавлені в подіях читання.
- writable()¶
Викликається кожного разу в асинхронному циклі, щоб визначити, чи слід додавати сокет каналу до списку, у якому можуть відбуватися події запису. Метод за замовчуванням просто повертає
True
, вказуючи, що за замовчуванням усі канали будуть зацікавлені в подіях запису.
Крім того, кожен канал делегує або розширює багато методів сокетів. Більшість із них майже ідентичні своїм партнерам по сокетам.
- create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM)¶
Це ідентично створенню звичайного сокета та використовуватиме ті самі параметри для створення. Зверніться до документації
socket
для отримання інформації про створення сокетів.Змінено в версії 3.3: Аргументи сімейство і тип можна опустити.
- connect(address)¶
Як і у випадку зі звичайним об’єктом сокета, адреса – це кортеж із першим елементом, до якого потрібно підключитися, а другим – номер порту.
- send(data)¶
Надіслати дані до віддаленої кінцевої точки сокета.
- recv(buffer_size)¶
Читати щонайбільше buffer_size байтів із віддаленої кінцевої точки сокета. Порожній об’єкт bytes означає, що канал закрито з іншого боку.
Зверніть увагу, що
recv()
може викликатиBlockingIOError
, навіть якщоselect.select()
абоselect.poll()
повідомили, що сокет готовий для читання.
- listen(backlog)¶
Прослухайте підключення до розетки. Аргумент backlog визначає максимальну кількість підключень у черзі та має бути принаймні 1; максимальне значення залежить від системи (зазвичай 5).
- bind(address)¶
Прив’яжіть сокет до адреси. Розетка ще не повинна бути прив’язаною. (Формат адреси залежить від сімейства адрес — зверніться до документації
socket
для отримання додаткової інформації.) Щоб позначити сокет як придатний для повторного використання (встановивши параметрSO_REUSEADDR
), викликати методset_reuse_addr()
об’єктаdispatcher
.
- accept()¶
Прийняти підключення. Сокет має бути прив’язаний до адреси та прослуховувати підключення. Поверненим значенням може бути
None
або пара(conn, address)
, де conn — це новий об’єкт сокета, який можна використовувати для надсилання та отримання даних під час з’єднання, а address — це адресу, прив’язану до сокета на іншому кінці з’єднання. Коли повертається «None», це означає, що з’єднання не відбулося, і в цьому випадку сервер повинен просто проігнорувати цю подію та продовжувати прослуховувати подальші вхідні з’єднання.
- close()¶
Закрийте розетку. Усі майбутні операції над об’єктом сокета не вдадуться. Віддалена кінцева точка більше не отримуватиме даних (після видалення даних із черги). Сокети автоматично закриваються під час збирання сміття.
- class asyncore.dispatcher_with_send¶
Підклас
dispatcher
, який додає просту можливість буферизованого виведення, корисну для простих клієнтів. Для більш складного використання використовуйтеasynchat.async_chat
.
- class asyncore.file_dispatcher¶
File_dispatcher приймає дескриптор файлу або file object разом із необов’язковим аргументом карти та обгортає його для використання з функціями
poll()
абоloop()
. Якщо надати файловий об’єкт або щось із методомfileno()
, цей метод буде викликано та передано конструкторуfile_wrapper
.Наявність: Unix.
- class asyncore.file_wrapper¶
File_wrapper приймає цілочисельний файловий дескриптор і викликає
os.dup()
, щоб дублювати дескриптор, щоб вихідний дескриптор можна було закрити незалежно від file_wrapper. Цей клас реалізує достатньо методів для емуляції сокета для використання класомfile_dispatcher
.Наявність: Unix.
asyncore Приклад базового клієнта HTTP¶
Ось простий клієнт HTTP, який використовує клас dispatcher
для реалізації обробки сокетів:
import asyncore
class HTTPClient(asyncore.dispatcher):
def __init__(self, host, path):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.connect( (host, 80) )
self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
(path, host), 'ascii')
def handle_connect(self):
pass
def handle_close(self):
self.close()
def handle_read(self):
print(self.recv(8192))
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
client = HTTPClient('www.python.org', '/')
asyncore.loop()
asyncore Приклад базового сервера відлуння¶
Ось базовий ехо-сервер, який використовує клас dispatcher
для прийняття з’єднань і відправляє вхідні з’єднання до обробника:
import asyncore
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
data = self.recv(8192)
if data:
self.send(data)
class EchoServer(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accepted(self, sock, addr):
print('Incoming connection from %s' % repr(addr))
handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()