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() -
Вызывается, когда возникает исключение и оно не обрабатывается иначе. Стандартная версия выводит сокращённый traceback.
-
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 принимает дескриптор файла или объект файла вместе с необязательным аргументом отображения и оборачивает его для использования с функциями
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–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.7/library/asyncore.html