Введение в WebSockets

WebSocket - это протокол компьютерной связи, обеспечивающий полнодуплексные каналы связи через одно TCP-соединение. - Википедия

В этом разделе вы узнаете, как настроить свой WebSocket на Python с помощью API WebSockets.

API Websocket делает возможным двусторонний интерактивный сеанс связи между клиентом и сервером. С помощью API вы можете отправлять и получать сообщения в режиме, управляемом событиями, без необходимости постоянно опрашивать сервер для получения данных. Это снижает накладные расходы и позволяет передавать данные в реальном времени с сервера и на сервер.

Начиная

WebSocket требует Python ≥ 3.6.1.

Вы можете просто установить API WebSockets в Python с помощью следующей команды:

pip install websockets

Прочитав эту статью, вы поймете, что такое WebSocket. Я собираюсь привести вам промежуточный пример, в котором сервер синхронизирует все сообщения, которые он получает, со своими подключенными клиентами.

Я не буду касаться таких тем, как безопасность - все, что я покажу, будет написано на Python. Это будет достаточно просто для понимания каждым, кто хоть немного знаком с языком или программированием в целом. Таким образом, вам будет проще написать потребителя, производителя или даже сервер на другом языке или для интерфейсного приложения.

Я надеюсь, что приведенные примеры будут вам полезны, и я призываю каждого разработчика попробовать WebSocket хотя бы раз в своей карьере - это здорово. Знаете, это больше, чем ОТДЫХ!

Простой потребитель сообщений

Итак, сначала давайте начнем с сопрограммы потребления, которую я предоставил выше. Я собираюсь объяснить каждую строчку приведенного выше кода, чтобы вы хорошо понимали, что происходит. Кратко резюмируя то, что происходит выше: мы подключаемся к WebSocket, указанному с помощью определенного URL-адреса WebSocket. Каждое сообщение, генерируемое сервером WebSocket, регистрируется.

Теперь я подробно объясню три наиболее важные строки. Не стесняйтесь пропустить это, если вас не интересует синтаксический сахар.

Вы можете быть немного напуганы, если никогда раньше не видели ключевые слова async или await. async/await - это просто специальный синтаксис для удобной работы с обещаниями. Обещание - это не что иное, как объект, представляющий окончательное завершение или сбой асинхронной операции.

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

В Python async гарантирует, что функция возвращает обещание, и обертывает в него не-обещания. Во время await вызовов может выполняться другой несвязанный код.

websocket_resource_url = f"ws://{host}:{port}"

URL-адрес ресурса WebSocket использует свою схему, начиная с ws (или wss для безопасного соединения). За ним следует имя хоста и номер порта (например, ws: //websocket.example.com: 8400). Я использую f-строку для создания URL ресурса здесь. Синтаксис аналогичен синтаксису, который вы использовали с str.format(), но строка f добавлена ​​в Python 3.6 и делает форматирование строкового литерала менее подробным.

async with websockets.connect(websocket_resource_url) as ws:

Следующая строка, которую я объясняю, открывает соединение с WebSocket с использованием websockets.connect. Ожидание подключения дает WebSocketClientProtocol, который затем можно использовать для отправки и получения сообщений. В этой строке используется async with, который работает с асинхронным диспетчером контекста. Соединение закрывается при выходе из контекста.

Примечание. Я иногда использовал аббревиатуру WebSocket (ws), чтобы сделать примеры кода более удобочитаемыми на Medium, но всегда пишу полное имя в производственном коде. Это увеличивает удобочитаемость. Например, его можно было бы прочитать как WebSite или WebServer, что вам, как хорошему разработчику, следует избегать. В конце концов, код должен читаться как хорошая книга.

async for message in websocket:

Вы можете подумать: «Подождите, а что делает async for?» Что ж, это похоже на синхронный цикл for, но он обеспечивает асинхронное понимание.

Асинхронный ввод-вывод позволяет нам перебирать асинхронный итератор. Это позволяет вам вызывать асинхронный код на каждом этапе, когда он повторяется, в то время как обычный цикл for не позволит вам этого сделать. Вы можете увидеть WebSocket в этой строке кода как генератор сообщений.

Чтобы запустить этого простого потребителя, просто укажите имя хоста и порт и заставьте его работать вечно. Это так просто. Не беспокойтесь, если цикла событий нет, asyncio создаст новый цикл событий и установит его как текущий.

Это просто, правда? Этот пример кода начнет получать сообщения от ws: // localhost: 4000. Если сервер не запущен, он завершится ошибкой и выдаст ошибку 404 Not Found.

Простой продюсер

Я собираюсь привести пример производителя, который производит только одно значение. Я покажу вам, что это даже проще, чем писать потребителю.

Приведенный выше код говорит сам за себя. Мы подключаемся к WebSocket, как мы это делали ранее в потребителе. Отправляем одно сообщение на сервер и ждем ответа. Когда мы получаем сообщение от сервера, мы знаем, что наше сообщение было доставлено.

Теперь нам нужен только способ выполнить эту производящую сопрограмму только один раз.

loop = asyncio.get_event_loop()
loop.run_until_complete(produce(message='hi', host='localhost', port=4000))

Конечно, мы даем ему название, и у Python есть на это ответ. Мы можем просто использовать цикл обработки событий так же, как и для потребителя. Единственная разница в том, что мы запускаем его, пока он не будет завершен. Как только мы получаем ответ от сервера, задача завершена.

В Python 3.7 это стало еще лучше - теперь мы можем использовать функцию run для выполнения сопрограммы. Довольно аккуратно, правда?

asyncio.run(produce(message='hi', host='localhost', port=4000))

Сервер: последний кусок головоломки

В этом случае я написал серверный класс, который объединяет все функции сервера вместе. Этот сервер распределяет сообщения, отправленные производителем, всем слушающим потребителям.

Сервер создается и определяет сопрограмму обработчика WebSocket. serve функция WebSocket - это оболочка для метода create_server() цикла обработки событий. Он создает и запускает сервер с create_server(). В качестве аргумента он принимает обработчик WebSocket.

Всякий раз, когда клиент подключается, сервер принимает соединение, создает WebSocketServerProtocol, выполняет открывающее рукопожатие и делегирует обработчику соединения, определенному параметром ws_handler. После того, как этот обработчик завершает работу, обычно или с исключением, сервер выполняет закрывающее рукопожатие и закрывает соединение.

Это все сделано за тебя. Поскольку мы указываем, что сервер должен работать вечно, он будет просто выполнять сопрограмму ws_handler, определенную в классе сервера (это объясняется ниже), каждый раз, когда наш производитель что-то создает. Затем он разошлет сообщение всем подключенным клиентам.

Последний фрагмент кода самый длинный, но он последний, так что держитесь.

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

Если есть подключенные клиенты, выполняется следующий фрагмент кода. asyncio.wait гарантирует, что мы продолжим работу только после того, как каждому клиенту будет отправлено сообщение.

Заключение

Итак, чтобы завершить все это, вот некоторые ключевые преимущества WebSockets по сравнению с длинным опросом HTTP:

  • Сообщение может быть отправлено любым способом в любое время в течение срока действия соединения WebSocket.
  • Клиент и сервер постоянно подключены - данные могут быть отправлены клиентам все время, без необходимости запрашивать их.
  • Работать с WebSockets на Python довольно просто. Этот пример синхронизации сообщений был реализован без необходимости написания большого количества кода. Сделать то же самое для длинного опроса HTTP было бы довольно сложно.