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()
Вернуться на верх