Документация Python неофициальный перевод
Содержание страницы

Потоки данныхStreams

Потоки данных – это высокоуровневые примитивы, готовые к использованию с async/await, для работы с сетевыми соединениями. Потоки данных позволяют отправлять и получать данные без использования колбэков или низкоуровневых протоколов и транспортов.

Ниже приведён пример TCP-эхо-клиента, написанного с использованием asyncio потоков данных:

import asyncio

async def tcp_echo_client(message):
    reader, writer = await asyncio.open_connection(
        '127.0.0.1', 8888)

    print(f'Send: {message!r}')
    writer.write(message.encode())

    data = await reader.read(100)
    print(f'Received: {data.decode()!r}')

    print('Close the connection')
    writer.close()
    await writer.wait_closed()

asyncio.run(tcp_echo_client('Hello World!'))

Смотрите также раздел Примеры ниже.

Функции потоков данных

Следующие функции asyncio верхнего уровня можно использовать для создания и работы с потоками данных:

coroutine asyncio.open_connection(host=None, port=None, *, loop=None, limit=None, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None)

Устанавливает сетевое соединение и возвращает пару объектов (reader, writer).

Возвращаемые объекты reader и writer являются экземплярами классов StreamReader и StreamWriter.

Аргумент loop необязателен и всегда определяется автоматически при ожидании этой функции из корутины.

Параметр limit определяет ограничение размера буфера, используемого возвращаемым экземпляром StreamReader. По умолчанию limit установлен в 64 КиБ.

Остальные аргументы передаются напрямую в loop.create_connection().

Новое в версии 3.7: параметр ssl_handshake_timeout.

coroutine asyncio.start_server(client_connected_cb, host=None, port=None, *, loop=None, limit=None, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)

Запускает сокет-сервер.

Колбэк client_connected_cb вызывается при каждом установлении нового подключения клиента. Он получает пару (reader, writer) в качестве двух аргументов – экземпляры классов StreamReader и StreamWriter.

client_connected_cb может быть обычным вызываемым объектом или корутинной функцией; если это корутинная функция, она будет автоматически запланирована как Task.

Аргумент loop необязателен и всегда определяется автоматически при ожидании этого метода из корутины.

Параметр limit определяет ограничение размера буфера, используемого возвращаемым экземпляром StreamReader. По умолчанию limit установлен в 64 КиБ.

Остальные аргументы передаются напрямую в loop.create_server().

Новое в версии 3.7: параметры ssl_handshake_timeout и start_serving.

Unix-сокеты

coroutine asyncio.open_unix_connection(path=None, *, loop=None, limit=None, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None)

Устанавливает соединение через Unix-сокет и возвращает пару (reader, writer).

Аналогично open_connection(), но работает с Unix-сокетами.

См. также документацию loop.create_unix_connection().

Доступность: Unix.

Новое в версии 3.7: параметр ssl_handshake_timeout.

Изменено в версии 3.7: Параметр path теперь может быть объектом, подобным пути

coroutine asyncio.start_unix_server(client_connected_cb, path=None, *, loop=None, limit=None, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True)

Запуск сервера Unix-сокетов.

Аналогично start_server(), но работает с Unix-сокетами.

См. также документацию loop.create_unix_server().

Доступность: Unix.

Новое в версии 3.7: параметры ssl_handshake_timeout и start_serving.

Изменено в версии 3.7: Параметр path теперь может быть path-подобным объектом.


StreamReader

class asyncio.StreamReader

Представляет объект чтения, предоставляющий API для чтения данных из потока ввода-вывода.

Не рекомендуется создавать объекты StreamReader напрямую; вместо этого используйте open_connection() и start_server().

coroutine read(n=-1)

Читает до n байт. Если n не указан или равен -1, читает до EOF и возвращает все прочитанные байты.

Если получен EOF и внутренний буфер пуст, возвращает пустой объект bytes.

coroutine readline()

Читает одну строку, где «строка» – это последовательность байтов, заканчивающаяся \n.

Если получен EOF и \n не найден, метод возвращает частично прочитанные данные.

Если получен EOF и внутренний буфер пуст, возвращается пустой объект bytes.

coroutine readexactly(n)

Прочитать ровно n байт.

Возбуждается IncompleteReadError, если EOF достигнут до того, как прочитано n байт. Используйте атрибут IncompleteReadError.partial, чтобы получить частично прочитанные данные.

coroutine readuntil(separator=b'\n')

Читает данные из потока до тех пор, пока не будет найден separator.

В случае успеха данные и разделитель удаляются из внутреннего буфера (потребляются). Возвращаемые данные включают разделитель в конце.

Если объем прочитанных данных превышает настроенный лимит потока, возбуждается исключение LimitOverrunError, а данные остаются во внутреннем буфере и могут быть прочитаны снова.

Если EOF достигнут до того, как найден полный разделитель, возбуждается исключение IncompleteReadError, и внутренний буфер сбрасывается. Атрибут IncompleteReadError.partial может содержать часть разделителя.

Новое в версии 3.5.2.

at_eof()

Возвращает True, если буфер пуст и был вызван feed_eof().

StreamWriter

class asyncio.StreamWriter

Представляет объект-писатель, предоставляющий API для записи данных в поток ввода-вывода.

Не рекомендуется создавать объекты StreamWriter напрямую; вместо этого используйте open_connection() и start_server().

can_write_eof()

Возвращает True, если нижележащий транспорт поддерживает метод write_eof(), и False в противном случае.

write_eof()

Закрывает конец потока для записи после сброса буферизованных данных записи.

transport

Возвращает базовый транспорт asyncio.

get_extra_info(name, default=None)

Предоставляет доступ к дополнительной информации о транспорте; подробнее см. BaseTransport.get_extra_info().

write(data)

Записывает data в поток.

Этот метод не подчиняется управлению потоком. Вызовы write() должны сопровождаться вызовом drain().

writelines(data)

Записывает в поток список (или любой итерируемый объект) байтов.

Этот метод не подчиняется управлению потоком. Вызовы writelines() должны сопровождаться вызовом drain().

coroutine drain()

Ожидает, когда можно будет возобновить запись в поток данных. Пример:

writer.write(data)
await writer.drain()

Это метод управления потоком, который взаимодействует с базовым буфером записи ввода-вывода. Когда размер буфера достигает верхней границы, drain() блокируется, пока размер буфера не уменьшится до нижней границы, после чего запись можно возобновить. Если ждать нечего, drain() возвращает управление немедленно.

close()

Закрывает поток.

is_closing()

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

Добавлено в версии 3.7.

coroutine wait_closed()

Ожидает закрытия потока данных.

Должен вызываться после close(), чтобы дождаться закрытия нижележащего соединения.

Добавлено в версии 3.7.

ПримерыExamples

TCP-эхо-клиент с использованием потоков данныхTCP echo client using streams

TCP-эхо-клиент с использованием функции asyncio.open_connection():

import asyncio

async def tcp_echo_client(message):
    reader, writer = await asyncio.open_connection(
        '127.0.0.1', 8888)

    print(f'Send: {message!r}')
    writer.write(message.encode())

    data = await reader.read(100)
    print(f'Received: {data.decode()!r}')

    print('Close the connection')
    writer.close()

asyncio.run(tcp_echo_client('Hello World!'))

См. также

Пример протокол TCP-эхо-клиента использует низкоуровневый метод loop.create_connection().

TCP-эхо-сервер с использованием потоков данныхTCP echo server using streams

TCP-эхо-сервер с использованием функции asyncio.start_server():

import asyncio

async def handle_echo(reader, writer):
    data = await reader.read(100)
    message = data.decode()
    addr = writer.get_extra_info('peername')

    print(f"Received {message!r} from {addr!r}")

    print(f"Send: {message!r}")
    writer.write(data)
    await writer.drain()

    print("Close the connection")
    writer.close()

async def main():
    server = await asyncio.start_server(
        handle_echo, '127.0.0.1', 8888)

    addr = server.sockets[0].getsockname()
    print(f'Serving on {addr}')

    async with server:
        await server.serve_forever()

asyncio.run(main())

См. также

Пример протокол TCP-эхо-сервера использует метод loop.create_server().

Получение HTTP-заголовковGet HTTP headers

Простой пример получения HTTP-заголовков URL, переданного в командной строке:

import asyncio
import urllib.parse
import sys

async def print_http_headers(url):
    url = urllib.parse.urlsplit(url)
    if url.scheme == 'https':
        reader, writer = await asyncio.open_connection(
            url.hostname, 443, ssl=True)
    else:
        reader, writer = await asyncio.open_connection(
            url.hostname, 80)

    query = (
        f"HEAD {url.path or '/'} HTTP/1.0\r\n"
        f"Host: {url.hostname}\r\n"
        f"\r\n"
    )

    writer.write(query.encode('latin-1'))
    while True:
        line = await reader.readline()
        if not line:
            break

        line = line.decode('latin1').rstrip()
        if line:
            print(f'HTTP header> {line}')

    # Игнорировать тело, закрыть сокет
    writer.close()

url = sys.argv[1]
asyncio.run(print_http_headers(url))

Использование:

python example.py http://example.com/path/page.html

или с HTTPS:

python example.py https://example.com/path/page.html

Регистрация открытого сокета для ожидания данных с использованием потоков данныхRegister an open socket to wait for data using streams

Корутина, ожидающая получения данных сокетом с помощью функции open_connection():

import asyncio
import socket

async def wait_for_data():
    # Получить ссылку на текущий цикл событий, так как
    # мы хотим получить доступ к низкоуровневым API.
    loop = asyncio.get_running_loop()

    # Создать пару соединённых сокетов.
    rsock, wsock = socket.socketpair()

    # Зарегистрировать открытый сокет для ожидания данных.
    reader, writer = await asyncio.open_connection(sock=rsock)

    # Симулировать приём данных из сети
    loop.call_soon(wsock.send, 'abc'.encode())

    # Ожидать данные
    data = await reader.read(100)

    # Данные получены, всё готово: закрыть сокет
    print("Received:", data.decode())
    writer.close()

    # Закрыть второй сокет
    wsock.close()

asyncio.run(wait_for_data())

См. также

Пример регистрация открытого сокета для ожидания данных с использованием протокола использует низкоуровневый протокол и метод loop.create_connection().

Пример наблюдения за файловым дескриптором на предмет событий чтения использует низкоуровневый метод loop.add_reader() для наблюдения за файловым дескриптором.