Spec-Zone.ru › Python 2.7

asyncore — Асинхронный обработчик сокетов

Исходный код: Lib/asyncore.py

Этот модуль предоставляет базовые средства для написания асинхронных клиентов и серверов на основе сокетов.

Существует только два способа выполнения «нескольких задач одновременно» на одном процессоре. Мультипоточное программирование — самый простой и популярный способ, но есть и другой, очень отличающийся подход, позволяющий получить практически все преимущества многопоточности без фактического использования нескольких потоков. Он действительно применим только если ваша программа в основном ограничена ввода-вывода. Если ваша программа ограничена процессором, то, скорее всего, вам понадобятся прерывистые планируемые потоки. Однако сетевые серверы редко ограничены процессором.

Если ваша операционная система поддерживает системный вызов 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_accept()

Подразумевается событием чтения на сокете прослушивания

Во время асинхронной обработки методы 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()

Вызывается, когда возникает исключение и не обрабатывается иначе. По умолчанию выводит сжатый traceback.

handle_accept()

Вызывается на каналах прослушивания (пассивные открыватели) при установлении соединения с новой удалённой точкой, которая выполнила вызов connect() для локальной точки.

readable()

Вызывается каждый раз при проходе асинхронного цикла, чтобы определить, следует ли добавлять сокет канала в список, на котором могут произойти события чтения. Метод по умолчанию просто возвращает True, указывая, что по умолчанию все каналы будут заинтересованы в событиях чтения.

writable()

Вызывается каждый раз при проходе асинхронного цикла, чтобы определить, следует ли добавлять сокет канала в список, на котором могут произойти события записи. Метод по умолчанию просто возвращает True, указывая, что по умолчанию все каналы будут заинтересованы в событиях записи.

Кроме того, каждый канал делегирует или расширяет многие методы сокета. Большинство из них почти идентичны своим сокет-аналогам.

create_socket(family, type)

Идентичен созданию обычного сокета и будет использовать те же параметры создания. Обратитесь к документации socket для получения информации о создании сокетов.

connect(address)

Как и для обычного объекта сокета, address — это кортеж, где первый элемент — хост для подключения, а второй — номер порта.

send(data)

Отправить data удалённой точке сокета.

recv(buffer_size)

Прочитать не более buffer_size байтов от удалённой точки сокета. Пустая строка подразумевает, что канал был закрыт с другой стороны.

Обратите внимание, что recv() может вызвать socket.error с EAGAIN или EWOULDBLOCK, даже если 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.

1. Пример asyncore: базовый HTTP-клиент

Вот очень базовый HTTP-клиент, который использует класс dispatcher для реализации обработки сокетов:

import asyncore, socket

class HTTPClient(asyncore.dispatcher):

    def __init__(self, host, path):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect( (host, 80) )
        self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path

    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()

2. Пример asyncore: базовый сервер эхо

Вот базовый сервер эхо, который использует класс dispatcher для приема подключений и перенаправляет входящие подключения обработчику:

import asyncore
import socket

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(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)

    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            sock, addr = pair
            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/2.7/library/asyncore.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API