В разделе рассмотрены низкоуровневые методы цикла событий модуля asyncio
, при помощи которых можно создавать асинхронные сетевые сервера следующих видов TCP-сервер, Unix-socket сервер.
Прежде чем что-то делать с циклом событий, его необходимо создать или получить функциями, описанными в разделе "Создание, запуск и получение цикла событий".
loop.create_server()
создает TCP-сервер,loop.create_unix_server()
создает Unix-сервер,loop.connect_accepted_socket()
оборачивает уже принятое соединение,asyncio.Server
,loop.create_server(protocol_factory, host=None, port=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, ssl_shutdown_timeout=None, start_serving=True)
:Метод loop.create_server()
создает TCP-сервер (сокет типа SOCK_STREAM
), прослушивающий порт port
адреса host
.
Метод loop.create_server()
представляет собой сопрограмму и возвращает объект Server
.
Аргументы:
protocol_factory
должен быть вызываемым объектом, который возвращает реализацию протокола модуля asyncio
.
host
может иметь несколько типов, которые определяют, где сервер будет прослушивать:
host
является строкой, то TCP-сервер привязан к одному сетевому интерфейсу, указанному host
.host
- это последовательность строк, то TCP-сервер привязан ко всем сетевым интерфейсам, указанным в последовательности.host
- пустая строка или None
, то предполагаются все интерфейсы, и будет возвращен список из нескольких сокетов. Скорее всего, один для IPv4, а другой - для IPv6.family
- для семейства можно задать значение AF_INET
или AF_INET6
, чтобы заставить сокет использовать IPv4 или IPv6. Если не задан, то семейство будет определяться по имени хоста (по умолчанию AF_UNSPEC
).
flags - это битовая маска для loop.getaddrinfo()
.
sock
можно дополнительно указать для использования уже существующего объекта сокета. Если указан sock
, то хост host
и порт port
указывать не нужно.
backlog
- это максимальное количество подключений в очереди, переданных в метод сокета Socket.listen()
(по умолчанию 100).
ssl
может быть установлен на экземпляр SSLContext
, чтобы включить TLS через принятые соединения.
reuse_address
сообщает ядру повторно использовать локальный сокет в состоянии TIME_WAIT
, не дожидаясь истечения его естественного тайм-аута. Если не указан, то в Unix будет автоматически установлено значение True
.
reuse_port
указывает ядру разрешить привязку этой конечной точки к тому же порту, к которому привязаны другие существующие конечные точки, если все они устанавливают этот флаг при создании. Эта опция не поддерживается в Windows.
ssl_handshake_timeout
- это (для TLS-сервера) время в секундах, в течение которого необходимо дождаться завершения подтверждения TLS, прежде чем разорвать соединение. Если не установлен то будет 60,0 секунд (по умолчанию).
ssl_shutdown_timeout
- это время в секундах, необходимое для ожидания завершения отключения SSL перед разрывом соединения. Если нет, то по умолчанию 30,0 секунд.
start_serving
, установленный в True (по умолчанию), то заставляет созданный сервер немедленно начать принимать соединения. Если установлено значение False
, то пользователь должен ждать на Server.start_serving()
или Server.serve_forever()
, чтобы сервер начал принимать соединения.
Новое в Python 3.7: Добавлены параметры ssl_handshake_timeout
и start_serving
.
Изменено в Python 3.6: Аргумент сокета TCP_NODELAY
установлен по умолчанию для всех TCP-соединений.
Изменено в версии 3.11: Добавлен аргумент ssl_shutdown_timeout
.
Смотрите также функцию asyncio.start_server()
- это альтернативный API более высокого уровня, который возвращает пару (StreamReader, StreamWriter
), которые можно использовать в коде async/await
.
loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True)
:Метод loop.create_unix_server()
работает так же как loop.create_server()
, но только с семейством сокетов AF_UNIX
.
Представляет собой сопрограмму и возвращает объект Server
.
path
- это имя сокета домена Unix и является обязательным, если не указан аргумент sock
. Поддерживаются абстрактные Unix сокеты, стоки str
, bytes
и объекты Path
.
Смотрите документацию метода loop.create_server()
для получения информации об остальных аргументах этого метода.
Доступность: Unix.
Новое в Python 3.7: добавлены параметры ssl_handshake_timeout
и start_serving
.
Изменено в Python 3.7: параметр path
теперь может быть объектом Path
.
Изменено в версии 3.11: Добавлен аргумент ssl_shutdown_timeout
.
loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)
:Метод loop.connect_accepted_socket()
оборачивает уже принятое соединение в пару (transport, protocol)
.
Представляет собой сопрограмму и возвращает пару (transport, protocol)
.
Метод может использоваться серверами, которые принимают соединения вне модуля asyncio
, но используют asyncio
для их обработки.
Аргументы:
protocol_factory
должен быть вызываемым объектом, который возвращает реализацию протокола модуля asyncio
.
sock
- это уже существующий объект сокета, возвращаемый из метода Socket.accept()
.
ssl
может быть установлен в SSLContext
, чтобы включить SSL через принятые соединения.
ssl_handshake_timeout
- это (для SSL-соединения) время в секундах, в течение которого необходимо дождаться завершения подтверждения SSL, прежде чем разорвать соединение. Если не установлен то будет 60,0 секунд (по умолчанию).ssl_shutdown_timeout
- это время в секундах, необходимое для ожидания завершения отключения SSL перед разрывом соединения. Если нет, то по умолчанию 30,0 секунд.
Изменено в Python 3.7: добавлен параметр ssl_handshake_timeout
.
Изменено в версии 3.11: Добавлен аргумент ssl_shutdown_timeout
.
Server
.Серверные объекты создаются функциями loop.create_server()
, loop.create_unix_server()
, start_server()
и start_unix_server()
.
Не создавайте экземпляр класса напрямую.
asyncio.Server
:Серверные объекты asyncio.Server
- это асинхронные диспетчеры контекста. При использовании в инструкции async with
гарантируется, что когда инструкция async with
завершена, то объект Server
закрыт и не принимает новые соединения:
srv = await loop.create_server(...) async with srv: # Какой-то код # На этом этапе `srv` закрыт и больше не принимает новые подключения.
Изменено в версии 3.7: Серверный объект является асинхронным диспетчером контекста, начиная с Python 3.7.
Server
:Server.close()
останавливает обслуживание сервера,Server.get_loop()
возвращает цикл событий сервера,Server.start_serving()
начинает принимать подключения,Server.serve_forever()
начинает принимать соединения,Server.is_serving()
проверяет, принимает ли сервер соединения,Server.wait_closed()
ждет завершения метода,Server.sockets
список объектов сокетов.Server.close()
:Метод Server.close()
останавливает обслуживание сервера: закрывает прослушивающие сокеты и устанавливает для атрибута сокетов значение None
.
Сокеты, которые представляют существующие входящие клиентские подключения, остаются открытыми.
Сервер закрывается асинхронно и чтобы дождаться закрытия сервера - используйте сопрограмму Server.wait_closed()
.
Server.get_loop()
:Метод Server.get_loop()
возвращает цикл событий, связанный с серверным объектом.
Новое в Python 3.7.
Server.start_serving()
:Метод Server.start_serving()
начинает принимать подключения.
Представляет собой сопрограмму.
Этот метод идемпотентен, поэтому его можно вызывать, когда сервер уже обслуживает соединения.
Ключевой аргумент start_serving
для методов loop.create_server()
и asyncio.start_server()
позволяет создать объект Server
, который изначально не принимает соединения. В этом случае можно использовать методы Server.start_serving()
или Server.serve_forever()
, чтобы сервер начал принимать соединения.
Новое в Python 3.7.
Server.serve_forever()
:Метод Server.serve_forever()
начинает принимать подключения, пока сопрограмма не будет отменена. Отмена задачи serve_forever приводит к закрытию сервера.
Представляет собой сопрограмму.
Этот метод можно вызвать, если сервер уже принимает соединения. На один объект Server
может существовать только одна задача Server.serve_forever()
.
async def client_connected(reader, writer): # Communicate with the client with # reader/writer streams. For example: await reader.readline() async def main(host, port): srv = await asyncio.start_server( client_connected, host, port) await srv.serve_forever() asyncio.run(main('127.0.0.1', 0))
Новое в Python 3.7.
Server.is_serving()
:Метод Server.is_serving()
возвращает True
, если сервер принимает новые соединения.
Новое в Python 3.7.
Server.wait_closed()
:Метод Server.wait_closed()
ждет завершения метода Server.close()
.
Представляет собой сопрограмму.
Server.sockets
:Атрибут Server.sockets
возвращает список объектов Socket
, которые прослушивает сервер.
Изменено в Python 3.7: до Python 3.7 метод Server.sockets
использовался для непосредственного возврата внутреннего списка серверных сокетов. В Python 3.7 возвращается копия этого списка.
Создадим TCP эхо-сервер с помощью метода loop.create_server()
, отправим обратно полученные данные и закроем соединение:
import asyncio class EchoServerProtocol(asyncio.Protocol): def connection_made(self, transport): peername = transport.get_extra_info('peername') print('Connection from {}'.format(peername)) self.transport = transport def data_received(self, data): message = data.decode() print('Data received: {!r}'.format(message)) print('Send: {!r}'.format(message)) self.transport.write(data) print('Close the client socket') self.transport.close() async def main(): # Получаем ссылку на цикл событий, т.к. планируем # использовать низкоуровневый API. loop = asyncio.get_running_loop() server = await loop.create_server( lambda: EchoServerProtocol(), '127.0.0.1', 8888) async with server: await server.serve_forever() asyncio.run(main())
Смотрите пример TCP эхо-сервера с использованием потоков stream
, который использует высокоуровневую функцию asyncio.start_server()
.
Пример TCP эхо-клиента использует метод loop.create_connection()
, отправляет данные и ждет, пока соединение не будет закрыто:
import asyncio class EchoClientProtocol(asyncio.Protocol): def __init__(self, message, on_con_lost): self.message = message self.on_con_lost = on_con_lost def connection_made(self, transport): transport.write(self.message.encode()) print('Data sent: {!r}'.format(self.message)) def data_received(self, data): print('Data received: {!r}'.format(data.decode())) def connection_lost(self, exc): print('The server closed the connection') self.on_con_lost.set_result(True) async def main(): # Получаем ссылку на цикл событий, т.к. планируем # использовать низкоуровневый API. loop = asyncio.get_running_loop() on_con_lost = loop.create_future() message = 'Hello World!' transport, protocol = await loop.create_connection( lambda: EchoClientProtocol(message, on_con_lost), '127.0.0.1', 8888) # Ждем, пока протокол не подаст сигнал о том, # что соединение потеряно, далее закроем транспорт. try: await on_con_lost finally: transport.close() asyncio.run(main())
Смотрите также пример TCP эхо-клиента с использованием потоков stream
, который использует высокоуровневую функцию loop.open_connection()
.