Содержание страницы
asyncore – Асинхронный обработчик сокетов¶asyncore – Asynchronous socket handler
Исходный код: Lib/asyncore.py
Устарело с версии 3.6, будет удалено в версии 3.12: Модуль asyncore устарел
(см. PEP 594 подробнее).
Вместо него используйте asyncio.
Примечание
Этот модуль существует только для обратной совместимости. Для нового кода
рекомендуется использовать asyncio.
Этот модуль предоставляет базовую инфраструктуру для написания клиентов и серверов асинхронных сокетных служб.
Доступность: not Emscripten, not WASI.
Этот модуль не работает или недоступен на платформах WebAssembly
wasm32-emscripten и wasm32-wasi. См.
платформы WebAssembly для получения дополнительной информации.
Есть только два способа заставить программу на одном процессоре делать «более одного дела одновременно». Многопоточное программирование – самый простой и популярный способ, но есть и другой, совершенно иной метод, который даёт почти все преимущества многопоточности без использования нескольких потоков. Он действительно практичен только в том случае, если программа в основном связана с вводом-выводом. Если программа загружает процессор, то, вероятно, на самом деле требуются вытесняющие потоки с планированием. Однако сетевые серверы редко бывают привязаны к процессору.
Если ваша операционная система поддерживает системный вызов 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()ed илиpoll()ed для событий чтения и записи.Таким образом, набор событий канала шире, чем базовые события сокета. Полный набор методов, которые можно переопределить в вашем подклассе, приведён ниже:
- 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 байт с удалённого узла сокета. Пустой объект bytes означает, что канал был закрыт с другого конца.
Обратите внимание, что
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.
asyncore Пример базового HTTP-клиента¶asyncore Example basic HTTP client
Вот очень простой 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 Пример простого эхо-сервера¶asyncore Example basic echo server
Вот простой эхо-сервер, который использует класс 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()