asyncore — Обработчик асинхронных сокетов
Исходный код: Lib/asyncore.py
Примечание
Этот модуль существует только для обратной совместимости. Для нового кода рекомендуется использовать asyncio.
Этот модуль предоставляет базовую инфраструктуру для написания асинхронных клиентов и серверов сокет-служб.
Существует всего два способа, чтобы программа на одном процессоре выполняла «несколько дел одновременно». Многопоточное программирование является самым простым и популярным способом, но существует и другой, очень отличающийся подход, который позволяет получить почти все преимущества многопоточности без фактического использования нескольких потоков. Он действительно практичен только если ваша программа в основном ориентирована на ввод-вывод. Если ваша программа ориентирована на процессор, то, вероятно, вам нужны потоки с прерыванием.
Если ваша операционная система поддерживает системный вызов select() в своей библиотеке ввода-вывода (а почти все поддерживают), то вы можете использовать его для одновременной работы с несколькими каналами связи; выполняя другие задачи, пока операции ввода-вывода выполняются «в фоновом режиме». Хотя эта стратегия может показаться странной и сложной, особенно на начальном этапе, она во многих отношениях легче понять и контролировать, чем многопоточное программирование. Модуль asyncore решает многие сложные проблемы за вас, делая задачу создания сложных высокопроизводительных сетевых серверов и клиентов легкой. Для «диалоговых» приложений и протоколов незаменимым является сопутствующий модуль asynchat.
Основная идея обоих модулей заключается в создании одного или нескольких сетевых каналов, экземпляров класса asyncore.dispatcher и asynchat.async_chat. Создание каналов добавляет их в глобальную карту, используемую функцией loop(), если вы не предоставите свою собственную карту.
После создания начального канала(ов), вызов функции loop() активирует обслуживание каналов, которое продолжается до закрытия последнего канала (включая любые, добавленные в карту во время асинхронного обслуживания).
-
asyncore.loop([timeout[, use_poll[, map[, count]]]]) -
Входит в цикл опроса, который завершается после count проходов или закрытия всех открытых каналов. Все аргументы необязательны. Параметр count по умолчанию равен
None, что приводит к завершению цикла только при закрытии всех каналов. Аргумент timeout устанавливает параметр таймаута для соответствующего вызоваselect()илиpoll(), измеряемого в секундах; по умолчанию составляет 30 секунд. Параметр use_poll, если он true, указывает, что следует использоватьpoll()вместоselect()(по умолчаниюFalse).Параметр map — это словарь, чьи элементы являются каналами для наблюдения. По мере закрытия каналов они удаляются из своей карты. Если map опущен, используется глобальная карта. Каналам (экземплярам
asyncore.dispatcher,asynchat.async_chatи их подклассам) разрешено свободно комбинироваться в карте.
-
class asyncore.dispatcher -
Класс
dispatcherпредставляет собой тонкий обёртку над объектом сокета низкого уровня. Для повышения полезности он имеет несколько методов обработки событий, которые вызываются из асинхронного цикла. В противном случае он может рассматриваться как обычный неблокирующий объект сокета.Срабатывание событий низкого уровня в определённые моменты или в определённых состояниях соединения сообщает асинхронному циклу о наступлении определённых событий высокого уровня. Например, если мы запросили подключение сокета к другому хосту, мы знаем, что соединение установлено, когда сокет впервые становится доступным для записи (в этот момент вы знаете, что можете записать в него с ожиданием успеха). Неявные события высокого уровня:
Событие Описание handle_connect()Подразумевается первым событием чтения или записи handle_close()Подразумевается событием чтения без доступных данных handle_accepted()Подразумевается событием чтения на сокете прослушивания Во время асинхронной обработки методы
readable()иwritable()каждого сопоставленного канала используются для определения того, должен ли сокет канала быть добавлен в список каналов,select()ых или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() -
Вызывается, когда возникает исключение и оно не обрабатывается иначе. По умолчанию выводится сокращённый traceback.
-
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: Аргументы family и type можно опустить.
-
connect(address) -
Как и в случае с обычным объектом сокета, address — это кортеж с первым элементом — хостом для подключения и вторым — номером порта.
-
send(data) -
Отправить data на удалённый узел сокета.
-
recv(buffer_size) -
Прочитать не более buffer_size байтов из удалённого узла сокета. Пустой объект байтов подразумевает, что канал был закрыт с другой стороны.
Обратите внимание, что
recv()может вызватьBlockingIOError, даже еслиselect.select()илиselect.poll()сообщили о готовности сокета к чтению.
-
listen(backlog) -
Прослушивать подключения, установленные к сокету. Аргумент backlog задаёт максимальное количество очередей подключений и должен быть по крайней мере 1; максимальное значение зависит от системы (обычно 5).
-
bind(address) -
Привязать сокет к address. Сокет не должен быть уже привязан. (Формат 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 принимает дескриптор файла или объект файла вместе с необязательным аргументом map и оборачивает его для использования с функциями
poll()илиloop(). Если предоставлен объект файла или что-либо с методомfileno(), этот метод будет вызван и передан в конструкторfile_wrapper. Доступность: UNIX.
-
class asyncore.file_wrapper -
file_wrapper принимает целочисленный дескриптор файла и вызывает
os.dup()для дублирования дескриптора, чтобы исходный дескриптор мог быть закрыт независимо от file_wrapper. Этот класс реализует достаточные методы для эмуляции сокета для использования классомfile_dispatcher. Доступность: UNIX.
1. Пример 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()
2. Пример 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()
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/asyncore.html