服务器主动推送之WebSocket的深入浅出

66 阅读4分钟
日期更新说明
2025年8月25日初版发布

序言

寄黄几复 黄庭坚

我居北海君南海,寄雁传书谢不能。 桃李春风一杯酒,江湖夜雨十年灯。 持家但有四立壁,治病不蕲三折肱。 想见读书头已白,隔溪猿哭瘴溪藤。

我住在北方海滨,而你住在南方海滨,欲托鸿雁传书,它却飞不过衡阳。 当年春风里观赏桃李共饮美酒,江湖落魄,一别已是十年,常对着孤灯听着秋雨思念着你。 你支撑生计也只有四堵空墙,艰难至此;你治理国家就像三折肱的良医一样,不需要去请求别人的经验。 想你清贫自守发奋读书,如今头发已白了罢,隔着充满瘴气的山溪,猿猴哀鸣攀援深林里的青藤。

  最近看韩跑跑接元婴去了,确实很感慨。其实韩天尊确实是凡人的映射。对于普通人来总有一种桃李春风一杯酒,江湖夜雨十年灯感觉,总是有种江湖一梦的惊愕。好看是好看,文章断更一周了;这篇文章本来是《服务器主动推送之SSE (Server-Sent Events)探讨》姊妹篇,迟迟没发布,今天终于要开始了。

概述

什么是WebSocket

  WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

  在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

从上图可以看出 websocket 有两部分组成:

  • 握手过程:这是从 HTTP 升级请求(Upgrade: websocket)到协议切换的“握手”过程
  • 数据帧:数据传输基本结构,详细参考:www.openmymind.net/WebSocket-F…
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

以上图表摘自 RFC 6455 WebSocket 协议,描述了一个 WebSocket 帧的结构。

其他特点包括:

  • 建立在 TCP 协议之上,服务器端的实现比较容易。
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  • 数据格式比较轻量,性能开销小,通信高效。
  • 可以发送文本,也可以发送二进制数据。
  • 没有同源限制,客户端可以与任意服务器通信。
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
ws://example.com:80/some/path

为什么选 WebSocket

WebSocket 的优点可以概括为:在需要高实时性高频双向数据交换的场景下,它能提供一种延迟极低、开销更小、更高效的通信方案,从而显著提升用户体验并降低服务器负载。

其特点在《服务器主动推送之SSE (Server-Sent Events)探讨》也提到过,这里不赘述。

实践

JSR 356(Java API for WebSocket)

对于 Javaer 来说生态确实不错,JSF 针对websocket推出JSR 356(Java API for WebSocket)。JSR 356于2013年随Java EE 7发布,是首个官方WebSocket规范,旨在解决不同容器(如Tomcat、Jetty)的私有API差异问题,提供跨容器的统一开发接口。

核心组件与架构

1. 核心注解

@OnOpen:连接建立时触发,用于初始化会话。 @OnMessage:接收客户端消息,支持文本/二进制数据。 @OnClose:连接关闭时清理资源。 @OnError:处理通信异常 @ServerEndpoint:声明服务端端点,指定URI路径(如@ServerEndpoint("/chat"))

2. 核心对象
  • Session:代表一个WebSocket连接,用于发送消息、管理会话属性(如用户ID)。
  • BasicRemote:同步发送消息(阻塞直至发送完成)。
    • AsyncRemote:异步发送(非阻塞,适合高并发)
    • BasicRemote:同步发送消息(阻塞直至发送完成)。
  • EndpointConfig:配置端点参数(如子协议、扩展)。

使用 SpringBoot 基于websockect聊天室案例

案例演示

案例代码

github.com/will-we/blo…