Содержание страницы
18.6. asyncore – асинхронный обработчик сокетов¶asyncore – Asynchronous socket handler
Исходный код: 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()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¶ A file_dispatcher takes a file descriptor or файловый объект along with an optional map argument and wraps it for use with the
poll()orloop()functions. If provided a file object or anything with afileno()method, that method will be called and passed to thefile_wrapperconstructor. Доступность: UNIX.
-
class
asyncore.file_wrapper¶ A file_wrapper takes an integer file descriptor and calls
os.dup()to duplicate the handle so that the original handle may be closed independently of the file_wrapper. This class implements sufficient methods to emulate a socket for use by thefile_dispatcherclass. Доступность: UNIX.
18.6.1. 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()
18.6.2. 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()