Рубрики
Без рубрики

Селекторы – I / O мультиплексирование абстракций

Автор оригинала: Doug Hellmann.

Цель:

Обеспечить независимые платформы абстракции для ввода/вывода мультиплексирование на основе

Выбрать

модуль.

Модуль SELECTORS обеспечивает независимую платформе, независимый от абстракции на верхней части функций мониторинга ввода-мониторинга, специфичной платформы в выборе.

Операционная модель

API в <Код> Селекторы на основе событий, аналогичны Poll () из Выбрать . Существует несколько реализаций, и модуль автоматически устанавливает псевдоним <код> defaultselector для обозначения наиболее эффективной для конфигурации текущей системы.

Объект селектора предоставляет методы уточнения каких событий для поиска на розетке, а затем позволяет вызывающему звонору дождаться событий в зависимости от платформы. Регистрация заинтересованности в событии создает <код> SELETERKEY , который содержит розетку, информацию о событиях интереса, и необязательные данные приложения. Владелец селектора вызывает свой выбрать () , чтобы узнать о событиях. Возвращаемое значение – это последовательность ключевых объектов и растрата, указывающая, какие события произошли. Программа, использующая селектор, должна неоднократно звонить <код> Выберите () , затем обрабатывать события соответствующим образом.

Echo server.

Пример ECHO Server ниже использует данные приложения в Selectorkey для регистрации функции обратного вызова на новом событии. Основной цикл получает обратный вызов из ключа и проходит к нему сокет и маска событий. В качестве сервера запускается, он регистрирует функцию ACCECT () , который будет вызываться для чтения событий на главной сокетке сервера. Принятие соединения создает новую розетку, которое затем зарегистрировано с функцией Code> Read () в качестве обратного вызова для чтения событий.

selectors_echo_server.py

import selectors
import socket

mysel  selectors.DefaultSelector()
keep_running  True


def read(connection, mask):
    "Callback for read events"
    global keep_running

    client_address  connection.getpeername()
    print('read({})'.format(client_address))
    data  connection.recv(1024)
    if data:
        # A readable client socket has data
        print('  received {!r}'.format(data))
        connection.sendall(data)
    else:
        # Interpret empty result as closed connection
        print('  closing')
        mysel.unregister(connection)
        connection.close()
        # Tell the main loop to stop
        keep_running  False


def accept(sock, mask):
    "Callback for new connections"
    new_connection, addr  sock.accept()
    print('accept({})'.format(addr))
    new_connection.setblocking(False)
    mysel.register(new_connection, selectors.EVENT_READ, read)


server_address  ('localhost', 10000)
print('starting up on {} port {}'.format(*server_address))
server  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
server.bind(server_address)
server.listen(5)

mysel.register(server, selectors.EVENT_READ, accept)

while keep_running:
    print('waiting for I/O')
    for key, mask in mysel.select(timeout1):
        callback  key.data
        callback(key.fileobj, mask)

print('shutting down')
mysel.close()

Когда READ () нет никаких данных из сокета, он интерпретирует событие чтения как на другую сторону подключения соединения вместо отправки данных. Он удаляет розетку от селектора и закрывает его. Чтобы избежать бесконечного цикла, этот сервер также отключается после того, как он завершит связь с одним клиентом.

Echo Client.

Пример клиента ECHO ниже обрабатывает все события ввода/вывода в основном цикле, а не используя обратные вызовы. Он устанавливает селектор, чтобы сообщить о чтении событий на сокете и сообщать, когда сокет готов к отправке данных. Поскольку он смотрит на два типа событий, клиент должен проверить, что произошло, изучив значение маски. После того, как все его исходящие данные были отправлены, он изменяет конфигурацию селектора только для получения отчета, когда есть данные для чтения.

selectors_echo_client.py

import selectors
import socket

mysel  selectors.DefaultSelector()
keep_running  True
outgoing  [
    b'It will be repeated.',
    b'This is the message.  ',
]
bytes_sent  0
bytes_received  0

# Connecting is a blocking operation, so call setblocking()
# after it returns.
server_address  ('localhost', 10000)
print('connecting to {} port {}'.format(*server_address))
sock  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(server_address)
sock.setblocking(False)

# Set up the selector to watch for when the socket is ready
# to send data as well as when there is data to read.
mysel.register(
    sock,
    selectors.EVENT_READ | selectors.EVENT_WRITE,
)

while keep_running:
    print('waiting for I/O')
    for key, mask in mysel.select(timeout1):
        connection  key.fileobj
        client_address  connection.getpeername()
        print('client({})'.format(client_address))

        if mask & selectors.EVENT_READ:
            print('  ready to read')
            data  connection.recv(1024)
            if data:
                # A readable client socket has data
                print('  received {!r}'.format(data))
                bytes_received  len(data)

            # Interpret empty result as closed connection,
            # and also close when we have received a copy
            # of all of the data sent.
            keep_running  not (
                data or
                (bytes_received and
                 (bytes_received  bytes_sent))
            )

        if mask & selectors.EVENT_WRITE:
            print('  ready to write')
            if not outgoing:
                # We are out of messages, so we no longer need to
                # write anything. Change our registration to let
                # us keep reading responses from the server.
                print('  switching to read-only')
                mysel.modify(sock, selectors.EVENT_READ)
            else:
                # Send the next message.
                next_msg  outgoing.pop()
                print('  sending {!r}'.format(next_msg))
                sock.sendall(next_msg)
                bytes_sent  len(next_msg)

print('shutting down')
mysel.unregister(connection)
connection.close()
mysel.close()

Клиент отслеживает количество данных, которое он отправил, и сумма, которую она получила. Когда эти значения совпадают и ненулены, клиент выходит из цикла обработки и чисто отключается, удаляя розетку от селектора и закрывающую как разъем, так и селектор.

Сервер и клиент вместе

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

$ python3 source/selectors/selectors_echo_server.py
starting up on localhost port 10000
waiting for I/O
waiting for I/O
accept(('127.0.0.1', 59850))
waiting for I/O
read(('127.0.0.1', 59850))
  received b'This is the message.  It will be repeated.'
waiting for I/O
read(('127.0.0.1', 59850))
  closing
shutting down

Вывод клиента показывает исходящее сообщение и ответ с сервера.

$ python3 source/selectors/selectors_echo_client.py
connecting to localhost port 10000
waiting for I/O
client(('127.0.0.1', 10000))
  ready to write
  sending b'This is the message.  '
waiting for I/O
client(('127.0.0.1', 10000))
  ready to write
  sending b'It will be repeated.'
waiting for I/O
client(('127.0.0.1', 10000))
  ready to write
  switching to read-only
waiting for I/O
client(('127.0.0.1', 10000))
  ready to read
  received b'This is the message.  It will be repeated.'
shutting down

Смотрите также