WebSocket

WebSocket概述

WebSocket提供全双工通信通道。

什么是WebSocket

// WebSocket特点:
// 1. 全双工通信:客户端和服务器可以同时发送消息
// 2. 低延迟:建立连接后,通信无需HTTP开销
// 3. 持久连接:一次握手,持续通信
// 4. 二进制支持:可以发送文本和二进制数据

// 与HTTP对比
// HTTP: 请求-响应模式,单向
// WebSocket: 双向通信,实时

// 应用场景
// - 实时聊天
// - 在线游戏
// - 股票行情
// - 协作编辑
// - 实时通知

WebSocket连接

创建和管理WebSocket连接。

创建连接

// 创建WebSocket连接
const ws = new WebSocket("wss://api.db-w.cn/ws");

// WebSocket URL格式
// ws://host:port/path    非加密
// wss://host:port/path   加密(推荐)

连接状态

const ws = new WebSocket("wss://api.db-w.cn/ws");

// WebSocket状态
console.log(ws.readyState);

// 状态值:
// 0: CONNECTING - 连接中
// 1: OPEN - 已连接
// 2: CLOSING - 关闭中
// 3: CLOSED - 已关闭

// 检查状态
function isConnected() {
    return ws.readyState === WebSocket.OPEN;
}

连接事件

const ws = new WebSocket("wss://api.db-w.cn/ws");

// 连接打开
ws.onopen = function(event) {
    console.log("连接已建立");
    // 发送消息
    ws.send("Hello Server");
};

// 连接关闭
ws.onclose = function(event) {
    console.log("连接已关闭");
    console.log("关闭码:", event.code);
    console.log("关闭原因:", event.reason);
    console.log("是否干净关闭:", event.wasClean);
};

// 连接错误
ws.onerror = function(error) {
    console.error("WebSocket错误:", error);
};

WebSocket消息

发送和接收消息。

发送消息

const ws = new WebSocket("wss://api.db-w.cn/ws");

ws.onopen = function() {
    // 发送文本
    ws.send("Hello Server");
    
    // 发送JSON
    ws.send(JSON.stringify({
        type: "message",
        content: "东巴文"
    }));
    
    // 发送二进制数据
    const buffer = new ArrayBuffer(4);
    const view = new DataView(buffer);
    view.setInt32(0, 1234);
    ws.send(buffer);
    
    // 发送Blob
    const blob = new Blob(["Hello"], { type: "text/plain" });
    ws.send(blob);
};

接收消息

const ws = new WebSocket("wss://api.db-w.cn/ws");

ws.onmessage = function(event) {
    // 消息数据
    console.log("收到消息:", event.data);
    
    // 检查数据类型
    if (typeof event.data === "string") {
        // 文本消息
        const message = JSON.parse(event.data);
        console.log(message);
    } else if (event.data instanceof Blob) {
        // Blob数据
        const reader = new FileReader();
        reader.onload = function() {
            console.log("Blob内容:", reader.result);
        };
        reader.readAsText(event.data);
    } else if (event.data instanceof ArrayBuffer) {
        // ArrayBuffer数据
        const view = new DataView(event.data);
        console.log(view.getInt32(0));
    }
};

二进制数据类型

const ws = new WebSocket("wss://api.db-w.cn/ws");

// 设置二进制类型
ws.binaryType = "blob";       // 接收Blob(默认)
ws.binaryType = "arraybuffer";// 接收ArrayBuffer

ws.onmessage = function(event) {
    if (event.data instanceof ArrayBuffer) {
        const view = new DataView(event.data);
        console.log(view.getInt32(0));
    }
};

WebSocket事件

WebSocket的事件处理。

事件监听

const ws = new WebSocket("wss://api.db-w.cn/ws");

// 使用addEventListener
ws.addEventListener("open", function(event) {
    console.log("连接打开");
});

ws.addEventListener("message", function(event) {
    console.log("收到消息:", event.data);
});

ws.addEventListener("close", function(event) {
    console.log("连接关闭:", event.code, event.reason);
});

ws.addEventListener("error", function(error) {
    console.error("连接错误:", error);
});

关闭码

// 常见关闭码
// 1000: 正常关闭
// 1001: 端点离开
// 1002: 协议错误
// 1003: 不支持的数据类型
// 1006: 异常关闭(没有收到关闭帧)
// 1007: 数据类型不一致
// 1008: 违反策略
// 1009: 消息过大
// 1010: 缺少扩展
// 1011: 内部错误
// 1012: 服务重启
// 1013: 稍后重试
// 3000-3999: 自定义保留
// 4000-4999: 应用自定义

ws.onclose = function(event) {
    switch (event.code) {
        case 1000:
            console.log("正常关闭");
            break;
        case 1006:
            console.log("连接异常断开");
            break;
        default:
            console.log("关闭:", event.code, event.reason);
    }
};

WebSocket心跳

保持连接活跃。

心跳机制

class WebSocketClient {
    constructor(url) {
        this.url = url;
        this.ws = null;
        this.heartbeatInterval = null;
        this.heartbeatTimeout = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
    }
    
    connect() {
        this.ws = new WebSocket(this.url);
        
        this.ws.onopen = () => {
            console.log("连接成功");
            this.reconnectAttempts = 0;
            this.startHeartbeat();
        };
        
        this.ws.onclose = () => {
            console.log("连接关闭");
            this.stopHeartbeat();
            this.reconnect();
        };
        
        this.ws.onerror = (error) => {
            console.error("连接错误:", error);
        };
        
        this.ws.onmessage = (event) => {
            this.handleMessage(event.data);
        };
    }
    
    startHeartbeat() {
        this.heartbeatInterval = setInterval(() => {
            if (this.ws.readyState === WebSocket.OPEN) {
                this.ws.send(JSON.stringify({ type: "ping" }));
                
                this.heartbeatTimeout = setTimeout(() => {
                    console.log("心跳超时,关闭连接");
                    this.ws.close();
                }, 5000);
            }
        }, 30000);
    }
    
    stopHeartbeat() {
        clearInterval(this.heartbeatInterval);
        clearTimeout(this.heartbeatTimeout);
    }
    
    handleMessage(data) {
        const message = JSON.parse(data);
        
        if (message.type === "pong") {
            clearTimeout(this.heartbeatTimeout);
            return;
        }
        
        console.log("收到消息:", message);
    }
    
    reconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
            
            console.log(`${delay}ms后重连...`);
            
            setTimeout(() => {
                this.connect();
            }, delay);
        }
    }
    
    send(data) {
        if (this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(data));
        }
    }
    
    close() {
        this.stopHeartbeat();
        this.ws.close(1000, "主动关闭");
    }
}

// 使用
const client = new WebSocketClient("wss://api.db-w.cn/ws");
client.connect();

Socket.IO

Socket.IO是WebSocket的增强库。

客户端使用

// 引入Socket.IO客户端
// <script src="/socket.io/socket.io.js"></script>

// 创建连接
const socket = io("https://api.db-w.cn");

// 连接事件
socket.on("connect", function() {
    console.log("连接成功");
});

socket.on("disconnect", function() {
    console.log("断开连接");
});

// 自定义事件
socket.on("message", function(data) {
    console.log("收到消息:", data);
});

socket.on("notification", function(data) {
    console.log("收到通知:", data);
});

// 发送消息
socket.emit("message", {
    content: "东巴文"
});

// 发送并等待确认
socket.emit("request", { id: 1 }, function(response) {
    console.log("服务器响应:", response);
});

// 加入房间
socket.emit("join", "room1");

// 离开房间
socket.emit("leave", "room1");

命名空间

// 连接不同命名空间
const chatSocket = io("/chat");
const newsSocket = io("/news");

chatSocket.on("message", function(data) {
    console.log("聊天消息:", data);
});

newsSocket.on("update", function(data) {
    console.log("新闻更新:", data);
});

房间和广播

// 服务端广播到房间
socket.emit("broadcast", {
    room: "lobby",
    message: "欢迎加入"
});

// 广播给所有人(除了自己)
socket.broadcast.emit("announcement", "新用户加入");

// 广播到房间(除了自己)
socket.to("room1").emit("message", "房间消息");

实际应用示例

实时聊天

class ChatClient {
    constructor(url) {
        this.ws = new WebSocket(url);
        this.messages = [];
        
        this.ws.onmessage = (event) => {
            const message = JSON.parse(event.data);
            this.handleMessage(message);
        };
    }
    
    handleMessage(message) {
        switch (message.type) {
            case "message":
                this.messages.push(message.data);
                this.renderMessages();
                break;
            case "user_join":
                console.log(`${message.data.name} 加入了聊天`);
                break;
            case "user_leave":
                console.log(`${message.data.name} 离开了聊天`);
                break;
        }
    }
    
    send(content) {
        this.ws.send(JSON.stringify({
            type: "message",
            content: content
        }));
    }
    
    renderMessages() {
        const container = document.getElementById("messages");
        container.innerHTML = this.messages
            .map(m => `<div>${m.user}: ${m.content}</div>`)
            .join("");
    }
}

// 使用
const chat = new ChatClient("wss://api.db-w.cn/chat");
chat.send("大家好!");

实时数据更新

class RealtimeData {
    constructor(url) {
        this.ws = new WebSocket(url);
        this.subscribers = new Map();
        
        this.ws.onmessage = (event) => {
            const data = JSON.parse(event.data);
            this.notify(data.channel, data.payload);
        };
    }
    
    subscribe(channel, callback) {
        if (!this.subscribers.has(channel)) {
            this.subscribers.set(channel, new Set());
            this.ws.send(JSON.stringify({
                type: "subscribe",
                channel: channel
            }));
        }
        
        this.subscribers.get(channel).add(callback);
        
        return () => {
            this.subscribers.get(channel).delete(callback);
        };
    }
    
    notify(channel, data) {
        const callbacks = this.subscribers.get(channel);
        if (callbacks) {
            callbacks.forEach(cb => cb(data));
        }
    }
}

// 使用
const realtime = new RealtimeData("wss://api.db-w.cn/realtime");

realtime.subscribe("stock:AAPL", (data) => {
    console.log("苹果股价:", data.price);
    updatePriceDisplay(data.price);
});

realtime.subscribe("news", (article) => {
    console.log("新闻:", article.title);
});

下一步

掌握了WebSocket后,让我们继续学习:

  1. ES6核心特性 - 学习ES6新特性
  2. ES7-ES12特性 - 学习新版本特性
  3. 迭代器与生成器 - 学习迭代协议

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:WebSocket是实现实时通信的核心技术,掌握心跳机制、重连策略和消息处理是构建实时应用的基础。在 db-w.cn,我们帮你掌握实时通信技术!