asyncore — Обработчик асинхронных сокетов
Исходный код: Lib/asyncore.py
Устарело начиная с версии 3.6: Пожалуйста, используйте asyncio вместо этого.
Примечание
Этот модуль существует только для обеспечения обратной совместимости. Для нового кода рекомендуется использовать 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() -
Вызывается, когда возникает исключение и оно не обрабатывается иначе. Стандартная версия выводит сжатый трассировку стека.
-
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 принимает дескриптор файла или объект файла вместе с необязательным аргументом карты и оборачивает его для использования с функциями
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()
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/asyncore.html