Java中的WebSocket:当HTTP力不从心时
\n\n想象一下在线股票交易所。你正盯着比特币的价格图表。每秒数字都在变化。或者一个多人在线游戏,你移动角色,而其他上百名玩家能立即看到。又或者只是一个企业聊天室,消息无需按F5就能收到。
\n\n这一切在底层是如何工作的?过去,开发者们想尽办法:轮询(客户端每秒向服务器询问“有消息吗?”)、长轮询(服务器保持连接直到有新消息)、服务器推送事件(单向数据流)。所有这些都是权宜之计。HTTP协议最初被设计为请求-响应模式。它无法主动“推送”数据。
\n\n这时WebSocket就派上了用场。这是一个协议,它建立一次连接——并保持其打开状态。服务器可以随时向客户端发送数据,反之亦然。实时双向通信,无需HTTP头部的开销。
\n\n在本文中,我们将全面剖析Java中的WebSocket。编写一个完整的聊天室,探讨潜在陷阱,并比较流行的库。如果您是一名Java开发者,想要构建Java实时应用——那么您来对地方了。
\n\nWebSocket如何工作?快速入门
\n\nWebSocket并非魔法,而只是一个基于TCP的协议。它以一个普通的HTTP请求(握手)开始,然后“升级”为一个持久的双向通道。
\n\n阶段1:握手
\n客户端发送一个带有Upgrade: websocket头部的普通HTTP GET请求。服务器以状态码101 Switching Protocols响应。就这样。从此刻起,数据不再以HTTP数据包的形式传输,而是作为WebSocket帧。
阶段2:数据帧
\n数据以小数据包(帧)的形式传输。帧可以是文本(普通字符串)或二进制(图片、音频)。Java库隐藏了这种复杂性——您只需处理字符串或字节。
\n\n阶段3:关闭
\n任何一方都可以通过发送一个特殊的Close帧来发起连接关闭。
\n\n听起来很简单?确实如此。但魔鬼在于细节:超时、重连、扩展。不过这些稍后再谈。
\n\nJava中的WebSocket库
\n\n在Java生态系统中,主要有三种方法:
\n\n- \n
- Jakarta WebSocket (JSR 356) — Java EE / Jakarta EE标准。可在任何兼容的服务器(Tomcat, Jetty, GlassFish)上运行。注解多,代码少。 \n
- Spring WebSocket — Spring生态系统中对标准的扩展。提供与Security、STOMP(基于WebSocket的协议,用于订阅主题)的集成以及便捷的模板。 \n
- Netty / Vert.x / Undertow — 底层异步框架。性能最高,但代码量更大。 \n
对于90%的任务,Jakarta或Spring就足够了。我们将通过实际示例来探讨这两种方案。
\n\n示例1:基于Jakarta WebSocket的简单聊天室(无Spring)
\n\n假设我们有一个老牌的Tomcat 10+(Jakarta EE)。我们需要一个服务器端点和一个客户端(HTML+JS)。
\n\n服务器:端点类
\nimport jakarta.websocket.*;\nimport jakarta.websocket.server.ServerEndpoint;\nimport java.io.IOException;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n@ServerEndpoint("/chat")\npublic class ChatEndpoint {\n\n // 存储所有活跃会话(线程安全集合)\n private static final Set<Session> sessions = new CopyOnWriteArraySet<>();\n\n @OnOpen\n public void onOpen(Session session) {\n sessions.add(session);\n broadcast("用户已连接。当前聊天室人数:" + sessions.size());\n }\n\n @OnMessage\n public void onMessage(String message, Session session) {\n // 可以添加逻辑:仅发送给