10.8. Điều khiển hai chiều với WebSockets¶
Server-Sent Events chỉ đẩy một chiều. Khi chủ nhân nhấn "lưu ảnh ngay bây giờ" hoặc "đặt lại bộ đếm kích hoạt" trên bảng điều khiển, bảng điều khiển phải gửi tin nhắn đến camera. Đó là WebSockets -- một socket TCP, các tin nhắn được đóng khung chạy theo cả hai hướng.
10.8.1. Route /control¶
microdot.websocket.with_websocket() thực hiện quy trình bắt tay nâng cấp WebSocket và chuyển cho handler một đối tượng WebSocket. Handler lặp vô tận, đọc các lệnh và gửi xác nhận:
from microdot.websocket import with_websocket
from microdot.websocket import WebSocketError
import json
@app.get('/control')
@with_websocket
async def control(request, ws):
while True:
try:
msg = await ws.receive()
except WebSocketError:
break
try:
cmd = json.loads(msg)
except ValueError:
await ws.send({'error': 'bad json'})
continue
if cmd.get('cmd') == 'snapshot_now':
if latest_jpeg:
path = '/sdcard/snaps/manual-{}.jpg'.format(
int(time.time()))
with open(path, 'wb') as f:
f.write(latest_jpeg)
await ws.send({'ok': True, 'saved': path})
else:
await ws.send({'ok': False, 'reason': 'no frame yet'})
elif cmd.get('cmd') == 'reset':
state['trigger_count'] = 0
await ws.send({'ok': True, 'counters': 'reset'})
else:
await ws.send({'error': 'unknown command'})
receive() trả về chuỗi cho các khung văn bản và bytes cho các khung nhị phân. WebSocket.send(...) ở phía trình duyệt gửi văn bản theo mặc định, vì vậy các lệnh được mã hóa JSON là lựa chọn tự nhiên.
send() chấp nhận chuỗi, bytes, hoặc bất kỳ thứ gì có thể serialize sang JSON -- một dict được gửi dưới dạng khung văn bản JSON.
WebSocketError được ném ra khi client ngắt kết nối (đóng sạch, mạng bị ngắt, hoặc lỗi giao thức). Handler thoát khỏi vòng lặp và trả về; microdot dọn dẹp socket.
10.8.2. Bảng điều khiển gửi lệnh¶
Hai nút được thêm vào index.html bên cạnh thanh trượt:
<button id="snap-btn">Save snapshot</button>
<button id="reset-btn">Reset counter</button>
và app.js mở WebSocket một lần và kết nối cả hai nút với send:
const proto = location.protocol === 'https:' ? 'wss://' : 'ws://';
const ws = new WebSocket(proto + location.host + '/control');
document.getElementById('snap-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'snapshot_now'})));
document.getElementById('reset-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'reset'})));
ws.addEventListener('message', (e) => {
console.log('cam:', JSON.parse(e.data));
});
Lựa chọn ws:// so với wss:// phản ánh http:// so với https:// -- WebSocket kế thừa cùng cách xử lý TLS. Với HTTPS được thiết lập, bảng điều khiển tự động kết nối qua wss://.
10.8.3. Khi nào nên chọn SSE so với WebSockets¶
Sử dụng SSE khi camera đẩy và trình duyệt chỉ lắng nghe -- thông báo, dữ liệu đo từ xa, thay đổi trạng thái. Đường truyền là HTTP thuần túy, phía client chỉ cần một dòng (new EventSource), và kết nối lại là tự động.
Sử dụng WebSockets khi trình duyệt cũng cần đẩy -- các nút, thanh trượt gửi cập nhật với tốc độ gõ phím, bất kỳ thứ gì mà việc chờ yêu cầu tiếp theo sẽ quá chậm. Kết nối là hai chiều và có đóng khung, nhưng API phức tạp hơn ở cả hai phía.
Camera bây giờ là một thiết bị tương tác -- xem, đẩy sự kiện ra, nhận lệnh vào.