WebSocket thời gian thực trong web

WebSocket là gì?

WebSocket là giao thức truyền thông hai chiều (full-duplex) qua một kết nối TCP duy nhất, cho phép server và client trao đổi dữ liệu thời gian thực mà không cần client liên tục gửi yêu cầu (polling). Khác với HTTP truyền thống dựa trên mô hình request-response, WebSocket thiết lập kết nối bền vững sau handshake ban đầu, sau đó cả hai phía có thể đẩy dữ liệu ngay lập tức.

Tại sao cần WebSocket?

Các ứng dụng hiện đại như chat, bảng giá chứng khoán, game multiplayer, hoặc bảng điều khiển IoT yêu cầu cập nhật dữ liệu trong mili-giây. HTTP polling (gửi request định kỳ) tốn băng thông, tăng độ trễ và tải server. WebSocket giải quyết bằng cách giữ kết nối mở, server đẩy (push) dữ liệu đến client ngay khi có thay đổi.

Cách WebSocket hoạt động

  1. Handshake: Client gửi HTTP request với header Upgrade: websocket. Server phản hồi 101 Switching Protocols nếu chấp nhận.
  2. Kết nối bền vững: Sau handshake, kết nối TCP vẫn mở, cả hai phía gửi frame nhị phân hoặc text.
  3. Ping/Pong: Cơ chế heartbeat để phát hiện kết nối đứt gãy.
  4. Đóng kết nối: Bất kỳ phía nào gửi frame Close, phía kia phản hồi và đóng TCP.
WebSocket connection diagram showing client server handshake

So sánh WebSocket, HTTP Polling, Server-Sent Events (SSE)

Đặc điểm HTTP Polling SSE WebSocket
Chiều truyền Client → Server Server → Client Hai chiều
Kết nối Ngắn, lặp lại Bền vững (một chiều) Bền vững (hai chiều)
Overhead Cao (header mỗi request) Thấp Rất thấp (frame 2 byte)
Auto-reconnect Tự xây dựng Có sẵn (EventSource) Cần tự xử lý
Phù hợp Cập nhật hiếm Feed một chiều Real-time hai chiều

Triển khai WebSocket với JavaScript (Client)

const ws = new WebSocket('wss://api.example.com/ws');

ws.onopen = () => {
    console.log('Kết nối đã mở');
    ws.send(JSON.stringify({type: 'subscribe', channel: 'prices'}));
};

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Nhận:', data);
    // Cập nhật UI
};

ws.onclose = () => {
    console.log('Đã đóng, thử kết nối lại sau 3s...');
    setTimeout(() => location.reload(), 3000);
};

ws.onerror = (err) => console.error('Lỗi:', err);

Triển khai WebSocket Server với Node.js (ws library)

const WebSocket = require('ws');
const wss = new WebSocket.Server({port: 8080});

wss.on('connection', (ws, req) => {
    console.log('Client mới kết nối');

    ws.on('message', (msg) => {
        const data = JSON.parse(msg);
        if (data.type === 'subscribe') {
            ws.channel = data.channel;
        }
    });

    // Gửi dữ liệu định kỳ (ví dụ: giá cổ phiếu)
    const interval = setInterval(() => {
        if (ws.readyState === WebSocket.OPEN) {
            ws.send(JSON.stringify({
                channel: ws.channel,
                price: (Math.random() * 100).toFixed(2),
                time: Date.now()
            }));
        }
    }, 1000);

    ws.on('close', () => clearInterval(interval));
});
WebSocket frame header structure RFC 6455

Xử lý reconnection và scaling

Kết nối WebSocket có thể đứt do mạng, proxy, hoặc server restart. Client cần logic reconnect với exponential backoff:

function connectWithRetry(url, retries = 0) {
    const ws = new WebSocket(url);
    ws.onclose = () => {
        const delay = Math.min(1000 * 2 ** retries, 30000);
        setTimeout(() => connectWithRetry(url, retries + 1), delay);
    };
    return ws;
}

Với hệ thống lớn, dùng Redis Pub/Sub hoặc message queue (RabbitMQ, Kafka) để đồng bộ message giữa nhiều instance server WebSocket. Sticky sessions hoặc shared state đảm bảo message đạt đúng client.

Bảo mật WebSocket

  • Luôn dùng wss:// (WebSocket over TLS) trong production.
  • Xác thực qua token trong handshake (header Authorization hoặc query param).
  • Validate và sanitize mọi message nhận được từ client.
  • Rate limit số lượng connection và message/giây mỗi IP.

Khi nào KHÔNG nên dùng WebSocket?

  • Chỉ cần cập nhật một chiều hiếm khi → SSE nhẹ hơn.
  • Ứng dụng CRUD truyền thống, không real-time → REST API đủ.
  • Client behind corporate proxy chặn WebSocket → cần fallback polling.

Tài liệu tham khảo

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Event-Driven Architecture: Kafka, RabbitMQ, Apache Pulsar

Event-Driven Architecture là gì? Event-Driven Architecture (EDA) là kiến trúc phần mềm xây dựng xung quanh sự kiện — các thay đổi trạng thái trong hệ thống được phát hiện,…

Xem thêm
Polars DataFrame featured image comparison chart

Polars DataFrame: Thay thế Pandas cho xử lý dữ liệu lớn hiệu năng cao

Polars DataFrame là gì? Polars DataFrame là thư viện xử lý dữ liệu hiệu năng cao viết bằng Rust, được thiết kế như một sự thay thế nhanh hơn và…

Xem thêm

Local-First Software & CRDT: Xây dựng app offline-first, sync không cần server trung tâm

Local-first software là triết lý thiết kế ứng dụng mà dữ liệu chính lưu trữ trên thiết bị người dùng trước, không phụ thuộc server trung tâm. Sync tự động…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất