凌峰创科服务平台

websocket 服务器推送

什么是 WebSocket 服务器推送?

WebSocket 服务器推送指的是服务器可以主动向已经连接的客户端(通常是浏览器)发送数据,而无需客户端先发起请求。

websocket 服务器推送-图1
(图片来源网络,侵删)

这解决了传统 Web 通信模式的根本性瓶颈。


为什么需要 WebSocket 服务器推送?(对比传统模式)

为了理解 WebSocket 的优势,我们先看看它出现之前的通信方式。

传统模式:HTTP 轮询 vs 长轮询

特性 HTTP 轮询 HTTP 长轮询 WebSocket
通信模式 客户端主动、服务器被动 客户端主动、服务器被动 双向、全双工
工作原理 客户端定时(如每秒)向服务器发送请求,询问是否有新数据。 客户端发送请求后,服务器保持连接打开,直到有新数据可返回或超时,然后立即发送响应并关闭连接,客户端再立即发起下一次请求。 客户端和服务器之间建立一个持久化的连接,任何一方都可以随时向另一方发送消息。
实时性 ,数据有延迟(取决于轮询间隔),且延迟不固定。 较好,有新数据时能较快返回,但建立新连接本身也有开销。 极好,毫秒级延迟,数据可实时推送。
服务器资源 消耗大,大量无效的短连接频繁建立和销毁,占用大量服务器资源和带宽。 消耗较大,虽然长连接减少了建连次数,但大量客户端同时保持连接会占用服务器资源。 消耗小,连接建立后保持长连接,通信开销极小,服务器资源利用率高。
数据格式 纯文本(如 HTML, JSON) 纯文本(如 HTML, JSON) ,支持文本和二进制,效率更高。
场景 简单、不要求实时性的场景 实时性要求稍高,但无法使用 WebSocket 的场景(如极旧浏览器) 聊天室、实时游戏、在线协作、金融行情、实时通知等高实时性场景。

WebSocket 通过一次握手建立一个持久的连接,彻底改变了服务器和客户端的通信关系,使得服务器能够像客户端一样主动“推送”信息,是实现真正实时通信的最佳选择。


WebSocket 如何工作?(握手与通信流程)

WebSocket 的通信分为两个阶段:握手数据传输

websocket 服务器推送-图2
(图片来源网络,侵删)

握手

握手过程是标准的 HTTP 请求/响应,目的是将 HTTP 升级为 WebSocket 协议。

  1. 客户端请求:浏览器向服务器发起一个特殊的 HTTP 请求,包含一个 Upgrade 头部。

    GET /chat HTTP/1.1
    Host: example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Version: 13
    Sec-WebSocket-Protocol: chat, superchat
  2. 服务器响应:服务器收到请求后,如果支持 WebSocket,则返回一个 101 Switching Protocols 状态码的响应,表示协议切换成功。

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

一旦握手完成,HTTP 连接就变成了一个 WebSocket 连接,后续的数据传输就不再遵循 HTTP 协议,而是遵循 WebSocket 自己定义的帧格式。

websocket 服务器推送-图3
(图片来源网络,侵删)

数据传输

连接建立后,服务器和客户端就可以自由地发送消息了,数据被分割成小的“帧”进行传输,这使得数据可以一边发送一边接收,并且可以处理大量数据而不会阻塞。


如何实现 WebSocket 服务器推送?(代码示例)

下面我们用几种主流的技术栈来展示如何实现一个简单的“服务器推送当前时间”的例子。

示例场景

客户端连接到服务器,服务器每秒向客户端推送一次当前的系统时间。

Node.js (使用 ws 库)

ws 是 Node.js 中最流行、性能最好的 WebSocket 库之一。

服务器端代码 (server.js)

// 1. 安装 ws: npm install ws
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 }); // 创建 WebSocket 服务器,监听 8080 端口
console.log('WebSocket server is running on ws://localhost:8080');
wss.on('connection', (ws) => {
  // 当有新的客户端连接时触发
  console.log('New client connected');
  // 2. 服务器主动推送数据
  const intervalId = setInterval(() => {
    const now = new Date().toLocaleTimeString();
    // 使用 ws.send() 方法向客户端发送消息
    ws.send(`Server time: ${now}`);
  }, 1000); // 每秒推送一次
  // 当客户端断开连接时触发
  ws.on('close', () => {
    console.log('Client disconnected');
    clearInterval(intervalId); // 清除定时器,避免内存泄漏
  });
  // 当收到客户端消息时触发
  ws.on('message', (message) => {
    console.log(`Received from client: ${message}`);
  });
});

客户端代码 (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">WebSocket Client</title>
</head>
<body>
    <h1>WebSocket Time Push</h1>
    <div id="output"></div>
    <script>
        // 3. 客户端连接服务器
        const socket = new WebSocket('ws://localhost:8080');
        // 当连接成功时触发
        socket.onopen = function(event) {
            console.log('Connected to WebSocket server');
            document.getElementById('output').innerHTML += '<p>Connected!</p>';
        };
        // 当收到服务器消息时触发
        socket.onmessage = function(event) {
            console.log('Message from server:', event.data);
            // 将收到的时间显示在页面上
            document.getElementById('output').innerHTML += `<p>${event.data}</p>`;
        };
        // 当连接关闭时触发
        socket.onclose = function(event) {
            console.log('Disconnected from WebSocket server');
            document.getElementById('output').innerHTML += '<p>Disconnected.</p>';
        };
        // 当发生错误时触发
        socket.onerror = function(error) {
            console.error('WebSocket Error:', error);
        };
    </script>
</body>
</html>

运行方式:

  1. 保存 server.js 并运行 node server.js
  2. index.html 放在任何一个 Web 服务器(如 Nginx, Apache 或简单的 python -m http.server)中,然后用浏览器打开它。
  3. 你将看到页面上每秒更新一次从服务器推送来的时间。

Go (使用 gorilla/websocket 库)

Go 语言以其高并发性能而闻名,非常适合构建 WebSocket 服务器。

服务器端代码 (main.go)

package main
import (
    "fmt"
    "log"
    "net/http"
    "time"
    "github.com/gorilla/websocket" // 需要先安装: go get github.com/gorilla/websocket
)
// 定义一个 upgrader,用于将 HTTP 连接升级为 WebSocket 连接
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true // 允许所有来源的连接,生产环境应更严格
    },
}
// 处理 WebSocket 连接的函数
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    // 升级 HTTP 连接
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close() // 确保连接关闭
    log.Println("New client connected")
    // 使用一个无限循环来持续向客户端推送数据
    for {
        // 获取当前时间
        currentTime := time.Now().Format("15:04:05")
        message := fmt.Sprintf("Server time: %s", currentTime)
        // 向客户端发送消息
        err := conn.WriteMessage(websocket.TextMessage, []byte(message))
        if err != nil {
            log.Println("Write error:", err)
            break // 如果发送失败,断开循环
        }
        // 等待一秒
        time.Sleep(1 * time.Second)
    }
}
func main() {
    // 设置 WebSocket 路由
    http.HandleFunc("/ws", handleWebSocket)
    // 启动 HTTP 服务器
    port := "8080"
    log.Printf("WebSocket server starting on :%s...", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

客户端代码与 Node.js 的示例完全相同。


WebSocket 的应用场景

几乎所有需要低延迟、高频率数据交换的场景都适合使用 WebSocket。

  • 即时通讯:微信、QQ、Slack 等聊天应用。
  • 实时协作:Google Docs、腾讯文档等多人在线编辑。
  • 在线游戏:服务器需要实时将玩家位置、状态等信息广播给所有玩家。
  • 金融数据:股票行情、外汇牌价的实时推送。
  • 实时通知:社交网络的新消息提醒、系统状态的实时更新。
  • 物联网:设备状态的实时监控和控制。

挑战与注意事项

  1. 兼容性:现代浏览器(Chrome, Firefox, Safari, Edge)对 WebSocket 的支持非常好,但如果需要支持非常旧的浏览器(如 IE10),需要做好兼容性检查。
  2. 服务器资源:虽然单个连接开销小,但成千上万的并发长连接会占用大量内存和文件描述符,需要合理配置服务器和进行负载均衡。
  3. 心跳机制:为了检测连接是否还活着,防止网络设备(如 NAT 路由器)因长时间无数据传输而切断连接,通常需要实现一个“心跳”机制(客户端和服务器定期发送简短的 Ping/Pong 消息)。
  4. 安全性
    • 使用 wss://:务必使用 WebSocket Secure (wss://),它通过 TLS/SSL 加密,防止数据被窃听。
    • 认证:在握手阶段或连接建立后,需要一套机制来验证客户端的身份(如 JWT Token, Cookie)。
  5. 可扩展性:当单台服务器无法承载所有连接时,需要考虑使用专门的 WebSocket 集群方案,
    • Nginx 作为反向代理和负载均衡器。
    • SocketClusterSocket.IO(虽然 Socket.IO 主要是为了兼容性,但也支持集群模式)等专门为高并发设计的框架。
    • 使用 Redis Pub/SubMQTT 等消息队列在多个 WebSocket 服务器实例间同步消息。

WebSocket 服务器推送是构建现代实时 Web 应用的基石,它通过建立一个持久的全双工连接,彻底改变了服务器与客户端的交互方式,实现了高效、低延迟的数据推送,掌握其原理和实现方式,对于开发高性能的实时应用至关重要。

分享:
扫描分享到社交APP
上一篇
下一篇