В разделе рассмотрены низкоуровневые методы цикла событий модуля asyncio
, при помощи которых можно создать и открыть сетевые потоковые соединения следующих видов TCP, UDP и Unix.
Прежде чем что-то делать с циклом событий, его необходимо создать или получить функциями, описанными в разделе "Создание, запуск и получение цикла событий".
loop.create_connection()
открывает TCP соединение,loop.create_datagram_endpoint()
открывает UDP соединение,loop.create_unix_connection()
открывает Unix соединение,loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, happy_eyeballs_delay=None, interleave=None)
:Метод loop.create_connection()
открывает TCP соединение с заданным адресом, указанным host
и port
. Представляет собой сопрограмму.
Семейство сокетов может быть AF_INET
или AF_INET6
в зависимости от хоста или аргумента family
, если он предоставлен.
Тип сокета будет SOCK_STREAM
.
Аргумент protocol_factory
должен быть вызываемым объектом, возвращающим реализацию протокола модуля asyncio
.
Метод loop.create_connection()
попытается установить соединение в фоновом режиме. В случае успеха - возвращает пару (transport, protocol)
.
Хронологический синопсис основной операции выглядит следующим образом:
protocol_factory
вызывается без аргументов и ожидается, что он вернет экземпляр протокола.Protocol.connection_made()
.(transport, protocol)
.Созданный транспорт представляет собой зависящий от реализации двунаправленный поток.
Другие аргументы:
ssl
:
False
, то создается транспорт SSL/TLS (по умолчанию создается простой транспорт TCP),ssl.SSLContext
, то этот контекст используется для создания транспорта,True
, то используется контекст по умолчанию, возвращаемый из ssl.create_default_context()
.server_hostname
устанавливает или переопределяет имя хоста, с которым будет сопоставляться сертификат целевого сервера. Следует устанавливать, только если ssl не равен None
. По умолчанию используется значение аргумента хоста. Если host
пуст (у него нет значения по умолчанию), то необходимо передать значение server_hostname
. Если server_hostname
является пустой строкой, то сопоставление имен хостов отключено. Это представляет собой серьезную угрозу безопасности, допускающую потенциальные атаки типа злоумышленник посередине.
family
, proto
, flags
- это необязательное семейство адресов, протокол и флаги, передаваемые в loop.getaddrinfo()
для разрешения хоста. Все они должны быть целыми числами из соответствующих констант модуля socket
.
happy_eyeballs_delay
, если задан, то для этого соединения включается алгоритм Happy Eyeballs. Это должно быть число float
, представляющее количество времени в секундах для ожидания завершения попытки подключения, прежде чем параллельно запускать следующую попытку. Как определено в RFC 8305 - это задержка попытки подключения, . Разумное значение по умолчанию, рекомендованное RFC, составляет 0,25 (250 миллисекунд).
Алгоритм Happy Eyeballs: это требования к алгоритмам, которые уменьшают видимую для пользователя задержку с хостами с двойным стеком. Другими словами, когда на сервере настроены два протокола IPv4/IPv6, а IPv6 не работает, то клиентское приложение с двойным стеком испытывает значительную задержку соединения по сравнению с клиентом, работающим только с IPv4. Это нежелательно, потому что из-за этого клиент с двойным стеком ухудшает работу пользователя.
Interleave
управляет переупорядочиванием адресов, когда имя хоста разрешается в несколько IP-адресов. Если 0 или не указано, то переупорядочивание не производится, и адреса проверяются в порядке, возвращаемом loop.getaddrinfo()
. Если задано положительное целое число, то адреса чередуются по семействам адресов, и данное целое число интерпретируется как счетчик первого семейства адресов, как определено в RFC 8305. Если аргумент happy_eyeballs_delay
не указан то устанавливается значение 0 (по умолчанию) или 1, если указан.
sock
, если он задан, то должен быть существующим, уже подключенным объектом socket.socket()
, который будет использоваться транспортом. Если аргумент sock
указан , то следующие аргументы указывать не надо: host
, port
, family
, proto
, flags
, happy_eyeballs_delay
, interleave
и local_addr
.
local_addr
, если задан, то должен является кортежем (local_host, local_port)
, используемым для локальной привязки сокета. Значения local_host
и local_port
ищутся с помощью loop.getaddrinfo()
, аналогично host
и port
.
ssl_handshake_timeout
- это (для TLS-соединения) время в секундах, в течение которого нужно дождаться завершения рукопожатия TLS, прежде чем разорвать соединение. По умолчанию - 60,0 секунд.
ssl_shutdown_timeout
- это время в секундах, необходимое для ожидания завершения отключения SSL перед разрывом соединения. 30,0 секунд, если нет (по умолчанию).
Изменено в Python 3.6: Параметр сокета TCP_NODELAY
устанавливается по умолчанию для всех соединений TCP.
Новое в Python 3.7: Добавлен параметр ssl_handshake_timeout
.
Новое в Python 3.8: Добавлены параметры happy_eyeballs_delay
и interleave
.
Изменено в версии 3.11: Добавлен параметр ssl_shutdown_timeout
.
Смотрите также функцию asyncio.open_connection()
- это высокоуровневый альтернативный API. Он возвращает пару (StreamReader, StreamWriter)
, которые могут быть использованы непосредственно в коде async/await
.
loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)
:Метод loop.create_datagram_endpoint()
создает UID соединение (соединение дейтаграммы). Представляет собой сопрограмму.
Семейство сокетов может быть AF_INET
, AF_INET6
или AF_UNIX
, в зависимости от хоста или аргумента family
, если он предоставлен.
Тип сокета будет SOCK_DGRAM
.
Аргумент protocol_factory
должен быть вызываемым объектом, возвращающим реализацию протокола модуля asyncio
.
В случае успеха - возвращает пару (transport, protocol)
Примечание. Аргумент
reuse_address
не поддерживается с версии Python 3.8.1 и полностью удален в Python 3.11, поскольку использованиеSO_REUSEADDR
представляет серьезную проблему безопасности для UDP соединений. Явная передачаreuse_address=True
вызовет исключение.
Когда несколько процессов с разными UID назначают сокеты на одинаковый адрес сокета UDP с помощью SO_REUSEADDR
, входящие пакеты могут случайным образом распределяться между сокетами.
Для поддерживаемых платформ, аргумент reuse_port
может использоваться как замена аналогичной функциональности. В случае reuse_port=True
, вместо него будет использоваться SO_REUSEPORT
, что специально предотвращает присвоение сокетам одного и того же адреса сокета процессам с разными UID.
Другие аргументы:
local_addr
, если задан, то представляет собой кортеж (local_host, local_port)
, используемый для локальной привязки сокета. Значения local_host
и local_port
ищутся с помощью loop.getaddrinfo()
.
remote_addr
, если задан, является кортежем (remote_host, remote_port)
, используемым для подключения сокета к удаленному адресу. Значения remote_host
и remote_port
ищутся с помощью loop.getaddrinfo()
.
family
, proto
, flags
- это необязательное семейство адресов, протокол и флаги, передаваемые в loop.getaddrinfo()
для разрешения хоста. Все они должны быть целыми числами из соответствующих констант модуля socket
.
reuse_port
указывает ядру разрешить привязку этой конечной точки к тому же порту, к которому привязаны другие существующие конечные точки, если все они устанавливают этот флаг при создании. Этот параметр не поддерживается в Windows и некоторых системах Unix. Если в системе константа SO_REUSEPORT
не определена, то эта возможность не поддерживается.
allow_broadcast
сообщает ядру разрешить этой конечной точке отправлять сообщения на широковещательный адрес.
sock
- это уже подключенный объект socket.socket()
, который будет использоваться транспортом. Если указаны, local_addr
и remote_addr
, то следует их опустить, т.е. должны быть None
.
Смотрите примеры протокола эхо-клиента UDP и примеры протокола эхо-сервера UDP.
Изменено в Python 3.8.1: аргумент reuse_address
больше не поддерживается из соображений безопасности.
Изменено в Python 3.8: Добавлена поддержка Windows.
Изменено в Python 3.11: аргумент reuse_address
, отключенный в Python 3.9.0, 3.8.1, 3.7.6 и 3.6.10, полностью удален.
loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)
:Метод loop.create_unix_connection()
создает соединение Unix. Представляет собой сопрограмму.
Семейство сокетов будет AF_UNIX
. Тип сокета будет SOCK_STREAM
.
В случае успеха возвращается кортеж (transport, protocol)
.
Аргумент path
- это имя сокета домена Unix и является обязательным, если не указан аргумент sock
. Поддерживаются абстрактные сокеты Unix, str, байты и пути Path.
Смотрите документацию по методу loop.create_connection()
для получения информации об остальных аргументах этого метода.
Доступность: Unix.
Новое в Python 3.7: добавлен аргумент ssl_handshake_timeout
.
Изменено в Python 3.7: теперь аргумент path
может быть объектом path
.
Изменено в Python 3.11: добавлен аргумент ssl_shutdown_timeout
.
UDP эхо-сервер, используя метод loop.create_datagram_endpoint()
, отправляет обратно клиенту полученные данные:
import asyncio class EchoServerProtocol: def connection_made(self, transport): self.transport = transport def datagram_received(self, data, addr): message = data.decode() print('Received %r from %s' % (message, addr)) print('Send %r to %s' % (message, addr)) self.transport.sendto(data, addr) async def main(): print("Starting UDP server") # Get a reference to the event loop as we plan to use # low-level APIs. loop = asyncio.get_running_loop() # One protocol instance will be created to serve all # client requests. transport, protocol = await loop.create_datagram_endpoint( lambda: EchoServerProtocol(), local_addr=('127.0.0.1', 9999)) try: await asyncio.sleep(3600) # Serve for 1 hour. finally: transport.close() asyncio.run(main())
UDP эхо-клиент, используя метод loop.create_datagram_endpoint()
, отправляет данные и закрывает транспорт, когда получает ответ:
import asyncio class EchoClientProtocol: def __init__(self, message, on_con_lost): self.message = message self.on_con_lost = on_con_lost self.transport = None def connection_made(self, transport): self.transport = transport print('Send:', self.message) self.transport.sendto(self.message.encode()) def datagram_received(self, data, addr): print("Received:", data.decode()) print("Close the socket") self.transport.close() def error_received(self, exc): print('Error received:', exc) def connection_lost(self, exc): print("Connection closed") self.on_con_lost.set_result(True) async def main(): # Get a reference to the event loop as we plan to use # low-level APIs. loop = asyncio.get_running_loop() on_con_lost = loop.create_future() message = "Hello World!" transport, protocol = await loop.create_datagram_endpoint( lambda: EchoClientProtocol(message, on_con_lost), remote_addr=('127.0.0.1', 9999)) try: await on_con_lost finally: transport.close() asyncio.run(main())