Какво е WebSocket и как да се внедри в Python?

WebSocket е мрежова технология, предоставена от HTML5 за пълнодуплексна комуникация между браузъри и сървъри. Това е усъвършенствана технология, която прави възможно отварянето на двупосочна интерактивна комуникация.

С WebSocket API можете да изпращате съобщения до отдалечен сървър и да получавате управлявани от събития отговори, без да се налага да търсите сървъра за отговор. В сравнение с HTTP, непостоянен протокол, WebSocket е протокол за постоянна мрежова комуникация.

Както показва диаграмата по-горе, тя не само позволява на клиента да поиска сървъра, но също така позволява на сървъра активно да изпраща данни на клиента. Това е истински двупосочен равен диалог, който принадлежи към един вид сървърна технология за натискане.

Функции на WebSocket

  • Въз основа на TCP протокола, внедряването от страна на сървъра е относително лесно.
  • Добра съвместимост с HTTP протокол. Портовете по подразбиране също са 80 и 443, а HTTP протоколът се използва във фазата на ръкостискане, така че не е лесно да се екранира по време на ръкостискането и може да преминава през различни HTTP прокси сървъри.
  • Форматът на данните е сравнително лек, разходите за производителност са малки и комуникацията е ефективна.
  • Могат да се изпращат както текстови, така и двоични данни.
  • Няма ограничение за същия произход и клиентите могат да комуникират с всеки сървър.
  • Идентификаторът на протокола е ws (или wss, ако е шифрован), а URL адресът на сървъра е URL адресът, например: ws(s)://test.com:80/some/path

Защо WebSocket

Без гражданство

В контекста на архитектурата на уеб приложение, HTTP е протокол за заявка-отговор, използван в модела клиент/сървър, при който клиентът изпраща HTTP заявка до сървър и сървърът отговаря с искания ресурс.

HTTP протоколът обаче е без състояние, т.е. третира всяка заявка като уникална и независима единица. Протоколите без състояние имат някои предимства, например сървърът не трябва да пази информация за сесията и следователно не трябва да съхранява данни.

Това също означава, че излишната информация за заявката се изпраща във всяка HTTP заявка и отговор, като например използването на бисквитки за проверка на потребителския статус.

Тъй като взаимодействието между клиенти и сървъри се увеличава, количеството информация, изисквано от HTTP протокола за комуникация между клиенти и сървъри, нараства бързо.

Полудуплекс

Друг недостатък е, че HTTP също е полудуплексен протокол, тоест потокът от информация в същото време може да бъде само еднопосочен: клиентът изпраща заявка до сървъра и сървърът отговаря на заявката. Този полудуплексен протокол прави ефективността на комуникацията ниска.

Инициализация на клиента

В същото време HTTP протоколът има недостатък: комуникацията може да бъде инициирана само от клиента. Следователно, ако сървърът има промяна на състоянието, той не може да уведоми клиента.

Заобиколни решения

За да се подобри ефективността на комуникацията на HTTP протокола, се прилагат следните методи:

  • Проучване: От време на време се прави заявка, за да се разбере дали сървърът има нова информация.
  • Дълго запитване: Клиентът изисква информация от сървъра и поддържа връзката за определен период от време.
  • Технология за поточно предаване: Клиентът изпраща заявка, а сървърът изпраща и поддържа отворен отговор, който непрекъснато се актуализира и поддържа отворен.

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

Ето защо „Майкъл Картър“ от група хора предложи WebSocket, което е естествена пълнодуплексна, двупосочна, едносокетна връзка, която решава проблема с HTTP протокола, който не е подходящ за комуникация в реално време.

Ръкостискане на WebSocket

Типично HTTP ръкостискане изглежда така:

GET / HTTP/2
Host: google.com
user-agent: curl/7.79.1
accept: */*
HTTP/2 301
location: https://www.google.com/
content-type: text/html; charset=UTF-8
date: Mon, 03 Oct 2022 17:50:02 GMT
expires: Wed, 02 Nov 2022 17:50:02 GMT
cache-control: public, max-age=2592000
server: gws
content-length: 220
x-xss-protection: 0
x-frame-options: SAMEORIGIN

Въпреки че самият WebSocket също е нов протокол на приложния слой, той не може да съществува независимо от HTTP. По-конкретно, ние изграждаме екземпляр на WebSocket на клиента и го свързваме с адрес на сървър, към който трябва да бъде свързан. Когато клиентът се свърже със сървъра, той ще изпрати на сървъра http съобщение, подобно на следното:

Host: 127.0.0.1:8081
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: 1wX9ZDz+x2c+7PoKBhr+eA==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
User-Agent: Python/3.10 websockets/10.3

Можете да видите, това е HTTP съобщение за заявка за получаване. Обърнете внимание, че в съобщението има заглавка за надстройка. Неговата функция е да каже на сървъра, че комуникационният протокол трябва да бъде превключен на WebSocket. Ако сървърът поддържа протокола WebSocket, протоколът се превключва към WebSocket и към клиента едновременно се изпраща заглавка на отговор, подобна на следната:

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: yBKlMEVMvp6dGL6qj4OH/T6zd5o=
Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=12; client_max_window_bits=12
Date: Mon, 03 Oct 2022 18:29:32 GMT
Server: Python/3.10 websockets/10.3

Можете да видите, че върнатият код за състояние е 101, което показва, че протоколът е преобразуван в WebSocket. Горният процес се извършва чрез HTTP комуникация, която се нарича WebSocket протокол Handshake. След тази точка клиентът и сървърът установяват WebSocket връзка и последващата комуникация се базира на WebSocket.

Внедряване на WebSocket Python

След като вече разбирате как работи WebSocket, нека се опитаме да внедрим WebSocket сървър с помощта на Python.

Инсталирайте websockets lib

Първо трябва да инсталираме websockets Python библиотека:

$ pip install websockets
Collecting websockets
  Downloading websockets-10.3-cp310-cp310-manylinuxwss5_x86_64.manylinux1_x86_64.manylinuxwss12_x86_64.manylinux2010_x86_64.whl (111 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 111.5/111.5 kB 1.9 MB/s eta 0:00:00
Installing collected packages: websockets
Successfully installed websockets-10.3web_server.py

сървър

import asyncio
import websockets
async def handler(ws, path):
    data = await ws.recv()
    reply = f"Data received: {data}"
    await ws.send(reply)
if __name__ == "__main__":
    start_server = websockets.serve(handler, "127.0.0.1", 8081)
    asyncio.get_event_loop().run_until_complete(start_server)
    asyncio.get_event_loop().run_forever()

Клиент

import asyncio
import websockets
async def connect():
    async with websockets.connect("ws://127.0.0.1:8081/") as websocket:
        await websocket.send("hello world")
        print(f"Reuqest headers:\n{websocket.request_headers}")
        response = await websocket.recv()
        print(f"Response headers:\n{websocket.response_headers}")
        print(response)
if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(connect())

За да стартирате като сървър:

$ python web_server.py 

като клиент:

$ python web_client.py 

Reuqest headers:
Host: 127.0.0.1:8081
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: 1wX9ZDz+x2c+7PoKBhr+eA==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
User-Agent: Python/3.10 websockets/10.3
Response headers:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: yBKlMEVMvp6dGL6qj4OH/T6zd5o=
Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=12; client_max_window_bits=12
Date: Mon, 03 Oct 2022 18:29:32 GMT
Server: Python/3.10 websockets/10.3
Data received: hello world