asyncore
— Обработчик асинхронных сокетов¶
Исходный код: Lib/asyncore.py.
Не рекомендуется, начиная с версии 3.6: asyncore
будет удален в Python 3.12 (подробнее см. PEP 594). Пожалуйста, используйте 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, если он равен 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: Аргументы семейство и тип могут быть опущены.
-
connect
(address)¶ Как и в обычном объекте сокета, адрес представляет собой кортеж, первый элемент которого - хост, к которому нужно подключиться, а второй - номер порта.
-
send
(data)¶ Отправка данных на удаленную конечную точку сокета.
-
recv
(buffer_size)¶ Считывание не более buffer_size байт с удаленного конца сокета. Пустой объект bytes означает, что канал был закрыт с другого конца.
Обратите внимание, что
recv()
может поднятьBlockingIOError
, даже еслиselect.select()
илиselect.poll()
сообщили, что сокет готов к чтению.
-
listen
(backlog)¶ Прослушать соединения, установленные на сокете. Аргумент backlog определяет максимальное количество соединений в очереди и должен быть не менее 1; максимальное значение зависит от системы (обычно 5).
-
bind
(address)¶ Привязать сокет к адресу. Сокет не должен быть уже привязан. (Формат address зависит от семейства адресов — за дополнительной информацией обратитесь к документации
socket
). Чтобы пометить сокет как повторно используемый (установив опциюSO_REUSEADDR
), вызовите методdispatcher
объектаset_reuse_addr()
.
-
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 вместе с необязательным аргументом map и оборачивает его для использования с функциями
poll()
илиloop()
. Если предоставлен объект файла или что-либо с методомfileno()
, этот метод будет вызван и передан конструкторуfile_wrapper
.Availability: Unix.
-
class
asyncore.
file_wrapper
¶ File_wrapper принимает целочисленный дескриптор файла и вызывает
os.dup()
для дублирования дескриптора, так что исходный дескриптор может быть закрыт независимо от file_wrapper. Этот класс реализует достаточно методов для эмуляции сокета для использования классомfile_dispatcher
.Availability: 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()