Какие протоколы может использовать WebSocket?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Протоколы, используемые WebSocket
WebSocket — это протокол, который работает поверх других протоколов, специально разработанный для полнодуплексного общения между клиентом и сервером в реальном времени.
1. WebSocket поверх HTTP/HTTPS (основной механизм)
Вебсокет использует HTTP для установления соединения, затем переходит на собственный протокол:
# На стороне сервера (FastAPI)
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
# HTTP Upgrade запрос
# GET /ws HTTP/1.1
# Upgrade: websocket
# Connection: Upgrade
# Sec-WebSocket-Key: ...
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f'Echo: {data}')
except Exception as e:
await websocket.close()
Процесс установления соединения:
1. Клиент отправляет HTTP GET запрос с заголовками:
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
2. Сервер отвечает 101 Switching Protocols:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
3. Теперь соединение переходит на WebSocket протокол
2. WebSocket поверх TCP (ws://)
Несекретный протокол WebSocket:
# Клиентский код (JavaScript)
const socket = new WebSocket('ws://localhost:8000/ws');
socket.onopen = () => {
console.log('Connected');
socket.send('Hello');
};
socket.onmessage = (event) => {
console.log('Received:', event.data);
};
Процесс:
1. Клиент устанавливает TCP соединение с сервером на порту 80 (по умолчанию)
2. Отправляет HTTP Upgrade запрос
3. Сервер отвечает с переключением протокола
4. Данные передаются в фреймах WebSocket
3. WebSocket поверх TLS/SSL (wss://)
Защищенный вебсокет, аналог HTTPS:
# Клиент подключается к защищенному вебсокету
const socket = new WebSocket('wss://example.com/ws');
# Это шифрует всю коммуникацию end-to-end
Преимущества wss://
# На сервере (Uvicorn с SSL)
# uvicorn app:app --ssl-keyfile=key.pem --ssl-certfile=cert.pem
from fastapi import FastAPI, WebSocket
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
# Все данные шифруются TLS
await websocket.accept()
data = await websocket.receive_text()
# Данные защищены от перехвата
await websocket.send_text(f'Secure: {data}')
Всегда использовать wss:// в production вместо ws://!
4. WebSocket фреймы (собственный протокол)
После установления соединения используется собственный формат фреймов:
Все данные разбиваются на фреймы:
Frame:
FIN (1 бит) - это последний фрейм?
opcode (4 бита) - тип данных (text=1, binary=2, close=8, ping=9, pong=10)
MASK (1 бит) - замаскированы ли данные?
payload length (7-63 бита) - длина данных
Mask key (32 бита, опционально)
Payload data - сами данные
# Пример с websockets модулем
import asyncio
import websockets
async def server(websocket, path):
async for message in websocket:
# Сервер получает распарсенный фрейм
print(f'Message: {message}')
await websocket.send(f'Echo: {message}')
async def main():
async with websockets.serve(server, 'localhost', 8765):
await asyncio.Future() # run forever
asyncio.run(main())
5. Типы фреймов WebSocket
from fastapi import WebSocket, WebSocketDisconnect
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
# 1. Text фрейм (opcode=1)
data = await websocket.receive_text()
print(f'Text: {data}')
# 2. Binary фрейм (opcode=2)
binary_data = await websocket.receive_bytes()
print(f'Binary: {binary_data}')
except WebSocketDisconnect:
# 3. Close фрейм (opcode=8)
print('Client disconnected')
await websocket.close(code=1000) # Normal closure
Opcodes (коды операций):
0x0 = Continuation frame
0x1 = Text frame
0x2 = Binary frame
0x8 = Connection close
0x9 = Ping
0xA = Pong
6. Ping/Pong механизм (keep-alive)
Для проверки живого соединения:
import asyncio
from fastapi import WebSocket, WebSocketDisconnect
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
async def keepalive():
while True:
try:
await websocket.send_json({'type': 'ping'})
await asyncio.sleep(30) # ping каждые 30 секунд
except:
break
asyncio.create_task(keepalive())
try:
while True:
data = await websocket.receive_text()
if data == 'pong':
continue # Connection is alive
await websocket.send_text(f'Echo: {data}')
except WebSocketDisconnect:
print('Client disconnected')
7. WebSocket поверх HTTP/2 (экспериментально)
Новый стандарт для лучшей производительности:
# Некоторые серверы начинают поддерживать WebSocket на HTTP/2
# Это дает лучшую производительность при количественных подключениях
# На данный момент это не стандартизировано,
# но работается над RFC для WebSocket on HTTP/2
8. Базовая конструкция WebSocket пакета
# Полный пример обмена
from fastapi import FastAPI, WebSocket
import json
app = FastAPI()
@app.websocket('/chat')
async def websocket_chat(websocket: WebSocket):
# 1. Handshake (HTTP Upgrade)
await websocket.accept()
try:
while True:
# 2. Получение текстового фрейма
data = await websocket.receive_text()
message = json.loads(data)
# 3. Отправка текстового фрейма обратно
response = {
'type': 'message',
'content': message['content'],
'timestamp': '2024-03-22T10:30:00Z'
}
await websocket.send_text(json.dumps(response))
# Можно также отправить бинарные данные
# await websocket.send_bytes(binary_data)
except Exception as e:
# 4. Close фрейм
await websocket.close(code=1000, reason=str(e))
9. Close коды WebSocket
from fastapi import WebSocket
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
data = await websocket.receive_text()
except:
pass
# Различные коды закрытия:
# 1000 - Normal closure
await websocket.close(code=1000, reason='Normal')
# 1001 - Going away
await websocket.close(code=1001)
# 1002 - Protocol error
await websocket.close(code=1002)
# 1003 - Unsupported data
await websocket.close(code=1003)
# 1006 - Abnormal closure (без фрейма)
# await websocket.close(code=1006)
# 1009 - Message too big
await websocket.close(code=1009)
# 1011 - Internal error
await websocket.close(code=1011, reason='Server error')
10. Сравнение с альтернативами
# WebSocket (двусторонний, low-latency)
@app.websocket('/ws')
async def ws(websocket: WebSocket):
await websocket.accept()
async for msg in websocket.iter_text():
await websocket.send_text(msg)
# Server-Sent Events (односторонний, легче для браузера)
@app.get('/sse')
async def sse():
async def event_generator():
for i in range(10):
yield f'data: Event {i}\n\n'
return StreamingResponse(event_generator(), media_type='text/event-stream')
# Polling (неэффективный)
@app.get('/poll')
async def poll():
return {'data': get_latest_updates()} # Клиент запрашивает каждую секунду
Итоговая таблица протоколов
| Протокол | Порт | Шифрование | Use Case |
|---|---|---|---|
| ws:// | 80 | Нет | Development, demo |
| wss:// | 443 | TLS/SSL | Production |
| HTTP Upgrade | 80/443 | Optional | Механизм установления |
| TCP | Кастомный | Нет | Кастомные протоколы |
| Фреймы WSP | - | Внутри TLS | Обмен данными |
Вся современная разработка должна использовать wss:// для защиты данных и обхода firewall'ов.