WebSocket实时通信入门到实践

引言

WebSocket 作为现代 Web 开发的"实时通信神器",已经成为构建交互式应用的标配技术。本文将带你从基础概念到高级应用,全面掌握 WebSocket 的核心技术,并解锁那些鲜为人知的高阶技巧!

一、告别轮询,拥抱实时

1.1 为什么需要 WebSocket?

传统 HTTP 的痛点:

  • 单向通信:只能客户端发起请求
  • 高延迟:需要不断轮询获取新数据
  • 资源浪费:每次请求都携带完整头部
javascript 复制代码
// 传统轮询示例
setInterval(async () => {
  const response = await fetch('/api/updates');
  // 处理数据...
}, 5000); // 每5秒请求一次

1.2 WebSocket 核心优势

  • 全双工通信:客户端和服务端可以同时发送消息
  • 低延迟:建立连接后即时传输
  • 高效:只需一次握手,后续通信头部极小

1.3 使用Express@wll8/express-ws创建一个基础的WebSocket服务端

javascript 复制代码
const express = require("express");
const expressWs = require(`@wll8/express-ws`);
const { app, wsRoute } = expressWs(express());

const port = 3000;

app.ws(`/`, (ws, req) => {
    // const {params, query} = req
    ws.send(`abc`);
    ws.on('message', (message) => {
        console.log('收到客户端消息:', message);
        ws.send(`正常连接`);
    });
});

// 定义一个简单的路由,当访问根路径时返回 "Hello, World!"
app.get("/", (req, res) => {
    res.send("Hello, World!");
});

// 启动服务器并监听指定端口
app.listen(port, () => {
    console.log(`服务器正在运行,访问地址为 http://localhost:${port}`);
});

1.4 基础客户端 WebSocket 连接

javascript 复制代码
const socket = new WebSocket("ws://127.0.0.1:3000");

// 连接打开
socket.onopen = () => {
    console.log("WebSocket 连接已建立");
    socket.send("Hello Server!");
};

// 接收消息
socket.onmessage = (event) => {
    console.log("收到消息:", event.data);
};

// 连接关闭
socket.onclose = () => {
    console.log("WebSocket 连接已关闭");
};

二、构建健壮的 WebSocket 应用

2.1 心跳检测机制

防止连接意外中断:

javascript 复制代码
let heartbeatInterval;
// 连接打开
socket.onopen = () => {
    console.log("WebSocket 连接已建立");
    socket.send("Hello Server!");
    heartbeatInterval = setInterval(() => {
        if (socket.readyState === WebSocket.OPEN) {
          socket.send(JSON.stringify({ type: 'heartbeat' }));
        }
      }, 15 * 1000); // 每15秒发送一次心跳
};


// 连接关闭
socket.onclose = () => {
    console.log("WebSocket 连接已关闭");
    clearInterval(heartbeatInterval);
};

2.2 消息序列化与协议设计

推荐消息格式

json 复制代码
{
  "id": "unique-message-id",
  "type": "message|notification|command",
  "timestamp": 1625097600000,
  "payload": {}
}

处理示例

javascript 复制代码
  // 接收消息
    socket.onmessage = (event) => {
        try {
            const message = JSON.parse(event.data);

            switch (message.type) {
                case "notification":
                    break;
                case "message":
                    console.log(message.payload);
                    break;
                default:
                    console.warn("未知消息类型:", message.type);
            }
        } catch (error) {
            console.error("消息解析失败:", error);
        }
    };

三、一个简单的聊天示例系统

3.1 前端实现

javascript 复制代码
class ChatClient {
    constructor(url) {
        this.socket = new WebSocket(url);
        this.setupEventHandlers();
    }

    setupEventHandlers() {
        this.socket.onmessage = (event) => {
            const message = JSON.parse(event.data);
            this.renderMessage(message);
        };

        this.socket.onclose = () => {
            console.log("关闭连接");
        };
    }

    sendMessage(text) {
        console.log(this.socket.readyState);
        if (this.socket.readyState === WebSocket.OPEN) {
            const message = {
                id: Math.floor(Math.random() * 10),
                type: "chat-message",
                timestamp: Date.now(),
                payload: { text },
            };
            this.socket.send(JSON.stringify(message));
        }
    }

    renderMessage(message) {
        let content = `${new Date(message.timestamp).toLocaleString()}:${message.payload.text}`;
        console.log(content);
    }
}

let client = new ChatClient("ws://127.0.0.1:3000");
setTimeout(() => {
    client.sendMessage("你好");
}, 1000);

4.2 服务端实现(Node.js)

javascript 复制代码
const express = require("express");
const expressWs = require(`@wll8/express-ws`);
const { app, wsRoute } = expressWs(express());

const port = 3000;

app.ws(`/`, (ws, req) => {
    // const {params, query} = req

    let msg = {
        id: "login",
        type: "message",
        timestamp: Date.now(),
        payload: {
            message: "消息",
        },
    };
    ws.send(JSON.stringify(msg));
    ws.on("message", (message) => {
        console.log("收到客户端消息:", message);
       // ws.send(`正常连接`);
    });
});

// 定义一个简单的路由,当访问根路径时返回 "Hello, World!"
app.get("/", (req, res) => {
    res.send("Hello, World!");
});

// 启动服务器并监听指定端口
app.listen(port, () => {
    console.log(`服务器正在运行,访问地址为 http://localhost:${port}`);
});

总结

如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript开发干货。

相关推荐
中微子4 小时前
🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱
前端·react.js
秋田君4 小时前
深入理解JavaScript设计模式之命令模式
javascript·设计模式·命令模式
中微子5 小时前
React 状态管理 源码深度解析
前端·react.js
风吹落叶花飘荡6 小时前
2025 Next.js项目提前编译并在服务器
服务器·开发语言·javascript
加减法原则6 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele6 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang4536 小时前
React移动端开发项目优化
前端·react.js·前端框架
你的人类朋友7 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
web_Hsir7 小时前
vue3.2 前端动态分页算法
前端·算法