asyncore
— Асинхронный обработчик сокетов
Не рекомендуется, начиная с версии 3.6: Вместо него используйте asyncio
.
Примечание
Этот модуль существует только для обратной совместимости. Для нового кода мы
рекомендуем использовать asyncio
.
Этот модуль обеспечивает базовую инфраструктуру для написания клиентов и серверов службы асинхронных сокетов.
Есть только два способа заставить программу на одном процессоре выполнять «более одной задачи одновременно». Многопоточное программирование — самый простой и самый популярный способ сделать это, но есть ещё один совершенно другой метод, который позволяет вам использовать почти все преимущества многопоточности, фактически не используя несколько потоков. Это действительно практично, только если ваша программа в значительной степени связана с вводом-выводом. Если ваша программа привязана к процессору, то, вероятно, вам действительно нужны упреждающие запланированные потоки. Однако сетевые серверы редко привязаны к процессору.
Если ваша операционная система поддерживает системный вызов 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()
илиpoll()
для событий чтения и записи.Таким образом, множество событий канала больше, чем базовых событий сокета. Полное множество методов, которые можно переопределить в вашем подклассе, приведён ниже:
-
handle_read
() Вызывается, когда асинхронный цикл обнаруживает, что вызов
read()
на сокете канала будет успешным.
-
handle_write
() Вызывается, когда асинхронный цикл обнаруживает, что доступный для записи сокет может быть записан. Часто этот метод реализует необходимую буферизацию для повышения производительности. Например:
def handle_write(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:]
-
handle_expt
() Вызывается при наличии внеполосных данных (out of band, 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.
Аsyncore пример базового 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('digitology.tech', '/')
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()