18.5.5. Потоки данныхAPI на основе корутин
Исходный код: Lib/asyncio/streams.py
18.5.5.1. Функции потоков данных
Примечание
Функции верхнего уровня в этом модуле представляют собой лишь обёртки для удобства; в них нет ничего особенного, и если они не выполняют именно то, что требуется, можно скопировать их код.
-
coroutine
asyncio.open_connection(host=None, port=None, *, loop=None, limit=None, **kwds)¶ Обёртка для
create_connection(), возвращающая пару (reader, writer).Возвращаемый reader является экземпляром
StreamReader, а writer – экземпляромStreamWriter.Все аргументы такие же, как обычно для
AbstractEventLoop.create_connection(), за исключением protocol_factory; наиболее часто используются позиционные параметры host и port, за которыми следуют различные необязательные ключевые аргументы.Дополнительные необязательные ключевые аргументы: loop (для указания экземпляра цикла событий) и limit (для установки лимита буфера, передаваемого в
StreamReader).Эта функция является корутиной.
-
coroutine
asyncio.start_server(client_connected_cb, host=None, port=None, *, loop=None, limit=None, **kwds)¶ Запускает сокет-сервер с колбэком для каждого подключённого клиента. Возвращаемое значение такое же, как у
create_server().Параметр client_connected_cb вызывается с двумя параметрами: client_reader и client_writer. client_reader – это объект
StreamReader, а client_writer – объектStreamWriter. Параметр client_connected_cb может быть как обычной функцией обратного вызова, так и корутинной функцией; если это корутинная функция, она автоматически преобразуется вTask.Остальные аргументы такие же, как обычно для
create_server(), за исключением protocol_factory; наиболее часто используются позиционные host и port, за которыми следуют различные необязательные ключевые аргументы.Дополнительные необязательные ключевые аргументы: loop (для указания экземпляра цикла событий) и limit (для установки лимита буфера, передаваемого в
StreamReader).Эта функция является корутиной.
-
coroutine
asyncio.open_unix_connection(path=None, *, loop=None, limit=None, **kwds)¶ Обёртка для
create_unix_connection(), возвращающая пару (reader, writer).См.
open_connection()для информации о возвращаемом значении и других деталях.Эта функция является корутиной.
Доступность: UNIX.
-
coroutine
asyncio.start_unix_server(client_connected_cb, path=None, *, loop=None, limit=None, **kwds)¶ Запускает сервер UNIX Domain Socket с колбэком для каждого подключённого клиента.
См.
start_server()для информации о возвращаемом значении и других деталях.Эта функция является корутиной.
Доступность: UNIX.
18.5.5.2. StreamReader
-
class
asyncio.StreamReader(limit=_DEFAULT_LIMIT, loop=None)¶ Этот класс является не потокобезопасным.
Значение аргумента limit по умолчанию равно _DEFAULT_LIMIT, то есть 2**16 (64 KiB).
-
exception()¶ Получить исключение.
-
feed_eof()¶ Подтверждает EOF.
-
feed_data(data)¶ Помещает байты data во внутренний буфер. Любые операции, ожидающие данные, будут возобновлены.
-
set_exception(exc)¶ Устанавливает исключение.
-
set_transport(транспорт)¶ Устанавливает транспорт.
-
coroutine
read(n=-1)¶ Читает до n байт. Если n не указан или равен
-1, читает до EOF и возвращает все прочитанные байты.Если был получен EOF и внутренний буфер пуст, возвращает пустой объект
bytes.Этот метод является корутиной.
-
coroutine
readline()¶ Читает одну строку, где «строка» – это последовательность байтов, заканчивающаяся на
\n.Если получен EOF, а
\nне найден, метод возвращает частично прочитанные байты.Если был получен EOF и внутренний буфер пуст, возвращает пустой объект
bytes.Этот метод является корутиной.
-
coroutine
readexactly(n)¶ Читает ровно n байт. Вызывает
IncompleteReadError, если конец потока достигнут до того, как можно прочитать n; атрибутIncompleteReadError.partialисключения содержит частично прочитанные байты.Этот метод является корутиной.
-
coroutine
readuntil(separator=b'\n')¶ Читает данные из потока до тех пор, пока не будет найден
separator.В случае успеха данные и разделитель удаляются из внутреннего буфера (потребляются). Возвращаемые данные будут включать разделитель в конце.
Настроенное ограничение потока используется для проверки результата. Ограничение задаёт максимальную длину данных, которые могут быть возвращены, не считая разделителя.
Если происходит EOF и полный разделитель всё ещё не найден, будет вызвано исключение
IncompleteReadError, а внутренний буфер будет сброшен. АтрибутIncompleteReadError.partialможет содержать разделитель частично.Если данные не удаётся прочитать из-за превышения ограничения, будет вызвано исключение
LimitOverrunError, а данные останутся во внутреннем буфере, чтобы их можно было прочитать снова.Новое в версии 3.5.2.
-
at_eof()¶ Возвращает
True, если буфер пуст и был вызванfeed_eof().
-
18.5.5.3. StreamWriter
-
class
asyncio.StreamWriter(транспорт, протокол, читатель, цикл событий)¶ Оборачивает транспорт.
Это предоставляет
write(),writelines(),can_write_eof(),write_eof(),get_extra_info()иclose(). Он добавляетdrain(), который возвращает необязательныйFuture, на котором можно ожидать управления потоком. Он также добавляет атрибут транспорта, который ссылается непосредственно наTransport.Этот класс является не потокобезопасным.
-
transport¶ Транспорт.
-
can_write_eof()¶ Возвращает
True, если транспорт поддерживаетwrite_eof(), иначеFalse. См.WriteTransport.can_write_eof().
-
close()¶ Закрывает транспорт: см.
BaseTransport.close().
-
coroutine
drain()¶ Даёт возможность сбросить буфер записи нижележащего транспорта.
Предполагаемое использование: записать:
pythonw.write(data) yield from w.drain()Когда размер буфера транспорта достигает верхнего предела (протокол приостановлен), блокирует выполнение до тех пор, пока размер буфера не уменьшится до нижнего предела и протокол не будет возобновлён. Если ожидать нечего, yield-from продолжается немедленно.
Yield from из
drain()даёт циклу событий возможность запланировать операцию записи и сбросить буфер. Особенно это следует использовать, когда в транспорт записывается возможно большой объём данных, и корутина не делает yield-from между вызовамиwrite().Этот метод является корутиной.
-
get_extra_info(имя, по умолчанию=None)¶ Возвращает дополнительную информацию о транспорте: см.
BaseTransport.get_extra_info().
-
write(данные)¶ Записывает некоторое количество байт данных в транспорт: см.
WriteTransport.write().
-
writelines(данные)¶ Записывает список (или любую итерируемую последовательность) байтов данных в транспорт: см.
WriteTransport.writelines().
-
write_eof()¶ Закрывает конец записи транспорта после сброса буферизованных данных: см.
WriteTransport.write_eof().
-
18.5.5.4. StreamReaderProtocol
-
class
asyncio.StreamReaderProtocol(читатель потока, колбэк подключения клиента=None, цикл событий=None)¶ Вспомогательный класс для адаптации между
ProtocolиStreamReader. ПодклассProtocol.stream_reader – это экземпляр
StreamReader, client_connected_cb – необязательная функция, вызываемая с (stream_reader, stream_writer) при установлении соединения, а loop – используемый экземпляр цикла событий.(Это вспомогательный класс, а не подкласс
ProtocolсамогоStreamReader, поскольку уStreamReaderесть и другие потенциальные применения, и чтобы пользовательStreamReaderслучайно не вызвал неподходящие методы протокола.)
18.5.5.5. IncompleteReadError
18.5.5.6. LimitOverrunError
18.5.5.7. Примеры использования потоков
18.5.5.7.1. TCP-эхо-клиент с использованием потоков
TCP-эхо-клиент с использованием функции asyncio.open_connection():
import asyncio
@asyncio.coroutine
def tcp_echo_client(message, loop):
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888,
loop=loop)
print('Send: %r' % message)
writer.write(message.encode())
data = yield from reader.read(100)
print('Received: %r' % data.decode())
print('Close the socket')
writer.close()
message = 'Hello World!'
loop = asyncio.get_event_loop()
loop.run_until_complete(tcp_echo_client(message, loop))
loop.close()
Смотрите также
В примере протокол TCP-эхо-клиента используется метод AbstractEventLoop.create_connection().
18.5.5.7.2. TCP-эхо-сервер с использованием потоков
TCP-эхо-сервер с использованием функции asyncio.start_server():
import asyncio
@asyncio.coroutine
def handle_echo(reader, writer):
data = yield from reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print("Received %r from %r" % (message, addr))
print("Send: %r" % message)
writer.write(data)
yield from writer.drain()
print("Close the client socket")
writer.close()
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_echo, '127.0.0.1', 8888, loop=loop)
server = loop.run_until_complete(coro)
# Обслуживать запросы до нажатия Ctrl+C
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Закрыть сервер
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
Смотрите также
Пример протокола TCP-эхо-сервера
использует метод AbstractEventLoop.create_server().
18.5.5.7.3. Получение HTTP-заголовков
Простой пример запроса HTTP-заголовков URL, переданного в командной строке:
import asyncio
import urllib.parse
import sys
@asyncio.coroutine
def print_http_headers(url):
url = urllib.parse.urlsplit(url)
if url.scheme == 'https':
connect = asyncio.open_connection(url.hostname, 443, ssl=True)
else:
connect = asyncio.open_connection(url.hostname, 80)
reader, writer = yield from connect
query = ('HEAD {path} HTTP/1.0\r\n'
'Host: {hostname}\r\n'
'\r\n').format(path=url.path or '/', hostname=url.hostname)
writer.write(query.encode('latin-1'))
while True:
line = yield from reader.readline()
if not line:
break
line = line.decode('latin1').rstrip()
if line:
print('HTTP header> %s' % line)
# Игнорировать тело, закрыть сокет
writer.close()
url = sys.argv[1]
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(print_http_headers(url))
loop.run_until_complete(task)
loop.close()
Использование:
python example.py http://example.com/path/page.html
или с HTTPS:
python example.py https://example.com/path/page.html
18.5.5.7.4. Регистрация открытого сокета для ожидания данных с использованием потоков
Корутина, ожидающая получения данных сокетом с помощью функции
open_connection():
import asyncio
try:
from socket import socketpair
except ImportError:
from asyncio.windows_utils import socketpair
@asyncio.coroutine
def wait_for_data(loop):
# Создать пару соединённых сокетов
rsock, wsock = socketpair()
# Зарегистрировать открытый сокет для ожидания данных
reader, writer = yield from asyncio.open_connection(sock=rsock, loop=loop)
# Симулировать приём данных из сети
loop.call_soon(wsock.send, 'abc'.encode())
# Ожидать данные
data = yield from reader.read(100)
# Получены данные, готово: закрыть сокет
print("Received:", data.decode())
writer.close()
# Закрыть второй сокет
wsock.close()
loop = asyncio.get_event_loop()
loop.run_until_complete(wait_for_data(loop))
loop.close()
Смотрите также
Пример регистрации открытого сокета для ожидания данных с использованием протокола использует низкоуровневый протокол, созданный методом
AbstractEventLoop.create_connection().
В примере наблюдение за файловым дескриптором на предмет событий чтения используется низкоуровневый метод AbstractEventLoop.add_reader() для регистрации файлового дескриптора сокета.