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开发干货。

相关推荐
xw51 小时前
免费的个人网站托管-Cloudflare
服务器·前端
网安Ruler1 小时前
Web开发-PHP应用&Cookie脆弱&Session固定&Token唯一&身份验证&数据库通讯
前端·数据库·网络安全·php·渗透·红队
!win !1 小时前
免费的个人网站托管-Cloudflare
服务器·前端·开发工具
饺子不放糖1 小时前
基于BroadcastChannel的前端多标签页同步方案:让用户体验更一致
前端
饺子不放糖1 小时前
前端性能优化实战:从页面加载到交互响应的全链路优化
前端
Jackson__1 小时前
使用 ICE PKG 开发并发布支持多场景引用的 NPM 包
前端
饺子不放糖1 小时前
前端错误监控与异常处理:构建健壮的Web应用
前端
cos1 小时前
FE Bits 前端周周谈 Vol.1|Hello World、TanStack DB 首个 Beta 版发布
前端·javascript·css
饺子不放糖1 小时前
CSS的float布局,让我怀疑人生
前端
阳光是sunny1 小时前
走进AI(1):细说RAG、MCP、Agent、Function Call
前端·ai编程