1. 什么是 WebSocket?
WebSocket 是一种网络通信协议,它在单个 TCP 连接上提供全双工通信(Full-Duplex Communication)的能力,允许服务器和客户端之间实时传输数据。
-
协议特点
- 全双工通信:服务器和客户端可以主动发送消息,而不是传统 HTTP 的请求-响应模式。
- 长连接:建立一次连接后,可以持续使用,直到一方主动关闭。
- 低延迟:消息通过帧(Frame)发送,减少了传统 HTTP 头信息带来的开销。
- 高效率:适合实时性要求高的场景。
-
工作原理
- 握手阶段:客户端通过 HTTP 发起 WebSocket 升级请求,服务端返回
101 Switching Protocols
,连接升级为 WebSocket 协议。 - 通信阶段:在连接建立后,数据通过轻量级的帧格式进行传输。
- 关闭阶段:一方主动发送关闭帧,连接断开。
- 握手阶段:客户端通过 HTTP 发起 WebSocket 升级请求,服务端返回
2. WebSocket 的作用
WebSocket 的主要作用是实现实时通信,即当数据在服务器或客户端发生变化时,可以立即推送到对方,而无需轮询(Polling)或长轮询(Long Polling)。
-
实时数据传输
- WebSocket 可以持续保持连接,无需频繁建立和关闭,适合快速传递实时数据。
-
服务器主动推送
- 服务器可以主动向客户端发送消息,减少客户端对服务器的频繁请求。
-
节省带宽和资源
- WebSocket 消息帧中没有冗余的 HTTP 头部信息,减少了带宽占用。
- 对于高频通信,WebSocket 比传统的 HTTP 更高效。
3. WebSocket 与 HTTP 的区别
特性 | HTTP(传统) | WebSocket(实时) |
---|---|---|
通信模型 | 请求-响应模型,客户端必须发起请求才能获得响应 | 双向通信,服务端和客户端都可以主动发送消息 |
连接状态 | 每次请求建立新的连接 | 连接建立后保持长连接 |
协议头开销 | 每次请求和响应都包含完整的 HTTP 头部信息 | 初次握手后,数据帧格式简单,无头部冗余 |
适用场景 | 低频通信、非实时性场景 | 高频通信、实时性要求高的场景 |
4. WebSocket 在 Java 开发中的使用场景
1.实时聊天系统
- 场景描述:聊天室、私聊、群聊。
- 实现功能:
- 用户之间实时发送和接收消息。
- 服务器广播消息给多个用户。
- 优势:相比轮询,WebSocket 的双向通信更高效。
2.实时通知和推送
- 场景描述:例如实时股票价格更新、系统消息推送、用户活动通知。
- 实现功能:
- 服务器可以主动向客户端推送数据。
- 优势:低延迟,用户体验更好。
3.实时数据流
- 场景描述:例如在线游戏的实时状态同步、物联网设备的数据流传输。
- 实现功能:
- 客户端和服务器持续同步状态或传输数据。
- 优势:节省带宽,支持大规模并发。
4.实时协作工具
- 场景描述:例如多人协作文档编辑、实时白板。
- 实现功能:
- 服务器实时广播用户的操作给其他参与者。
- 优势:多用户实时协作,延迟低。
5.在线教育
- 场景描述:实时课堂互动、学生提问、教师推送消息。
- 实现功能:
- 服务器推送教学内容或问题答案。
- 优势:实时互动,提升教学体验。
6.实时监控和仪表盘
- 场景描述:例如服务器运行状态、日志监控、设备运行状态监控。
- 实现功能:
- 实时向用户展示系统状态或日志信息。
- 优势:无需刷新页面即可获得最新状态。
7.在线游戏
- 场景描述:实时多人游戏的状态同步和交互。
- 实现功能:
- 玩家操作即时同步到服务器和其他玩家。
- 优势:低延迟、高效率。
5.WebSocket 在 Java 中的实现与支持工具
技术栈 | 归属 | 特点 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|---|
Socket.IO | 前端 | - 基于 WebSocket 的封装,支持事件驱动和自动降级机制 | 浏览器端实时通信,如聊天系统、通知推送、协作工具 | - 易用,前端开发体验优秀 - 跨浏览器兼容性强 | - 性能一般,依赖 Node.js 环境 |
javax.websocket | 后端 | - Java 官方 WebSocket 标准 API,轻量级 | 简单实时通信,如聊天室、通知推送 | - 简单易用,标准化 - 无框架依赖 | - 功能基础,缺乏高级特性 |
Spring WebSocket | 后端 | - 基于 javax.websocket 增强实现,支持 STOMP 和 SockJS 降级 | Spring Boot 项目中的实时通信,支持广播和订阅 | - 与 Spring 生态无缝集成 - 功能丰富 | - 性能稍逊于底层框架(如 Netty) |
Netty | 后端 | - 高性能网络通信框架,支持多协议(TCP、UDP、HTTP、WebSocket 等) | 高并发需求,如在线游戏、实时推送、物联网 | - 性能极高 - 灵活性强 | - 学习曲线陡峭 - 开发复杂 |
Netty-socketio | 后端 | - 基于 Netty 的封装框架,提供与 Socket.IO 类似的事件驱动模型 | 高并发实时通信,如实时聊天、事件推送 | - 性能高 - 开发简单,API 易用 | - 灵活性不如 Netty - 功能局限于 WebSocket |
6.在 Java 中使用 javax.websocket
实现实时通信
1. WebSocket 的使用步骤
在 Java 中使用 javax.websocket
实现实时通信时,需要完成以下步骤:
-
引入必要的依赖
WebSocket 的标准 API 是 Java EE 的一部分。需要引入javax.websocket
和 WebSocket 的实现(如Tyrus
)。 -
创建 WebSocket 服务端
使用@ServerEndpoint
注解定义 WebSocket 端点,处理客户端的连接、消息和断开事件。 -
配置 WebSocket 服务端
在 Spring Boot 中注册 WebSocket 端点,使其能与 Web 应用程序协同运行。 -
创建 WebSocket 客户端(可选)
使用@ClientEndpoint
注解定义客户端端点,用于连接服务端并实现双向通信。 -
启动服务并进行通信
测试服务端与客户端之间的实时消息交换。
2. 引入依赖
在 Spring Boot 项目中,引入以下依赖:
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version>
</dependency>
<dependency><groupId>org.glassfish</groupId><artifactId>tyrus-server</artifactId><version>1.17</version>
</dependency>
javax.websocket-api
提供 WebSocket API 接口。tyrus-server
是 WebSocket 的实现库,用于支持运行。
3. 创建 WebSocket 服务端
在 Java 中,使用 @ServerEndpoint
注解定义服务端。
完整代码示例
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;@ServerEndpoint("/websocket")
public class WebSocketServer {// 存储所有客户端会话private static final CopyOnWriteArraySet<WebSocketServer> clients = new CopyOnWriteArraySet<>();// 当前会话private Session session;// 当客户端连接成功时触发@OnOpenpublic void onOpen(Session session) {this.session = session; // 保存当前会话clients.add(this); // 添加到客户端集合System.out.println("新的连接已建立,当前连接数:" + clients.size());sendMessage("欢迎加入 WebSocket 通信!");}// 当收到客户端消息时触发@OnMessagepublic void onMessage(String message, Session session) {System.out.println("收到客户端消息:" + message);broadcast("服务器收到消息:" + message); // 广播给所有客户端}// 当连接关闭时触发@OnClosepublic void onClose(Session session) {clients.remove(this); // 从客户端集合中移除System.out.println("连接已关闭,当前连接数:" + clients.size());}// 当发生错误时触发@OnErrorpublic void onError(Session session, Throwable error) {System.err.println("发生错误:" + error.getMessage());}// 向当前客户端发送消息private void sendMessage(String message) {try {this.session.getBasicRemote().sendText(message); // 发送文本消息} catch (IOException e) {e.printStackTrace();}}// 向所有客户端广播消息private void broadcast(String message) {for (WebSocketServer client : clients) {try {client.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}}
}
代码详解
-
类注解 @ServerEndpoint
- 指定 WebSocket 服务端的路径为
/websocket
。 - 客户端通过
ws://localhost:8080/websocket
连接服务端。
- 指定 WebSocket 服务端的路径为
-
@OnOpen
方法- 在客户端连接成功时调用。
- 参数
Session
:表示与客户端的会话,用于发送消息或获取连接信息。 - 将当前会话保存到
clients
集合中,便于广播消息。
-
@OnMessage
方法- 在收到客户端消息时调用。
- 参数
message
:客户端发送的消息。 - 将消息打印到控制台,并广播给所有已连接的客户端。
-
@OnClose
方法- 在客户端关闭连接时调用。
- 从
clients
集合中移除当前会话。
-
@OnError
方法- 在通信过程中发生异常时触发。
- 可以记录日志或进行异常处理。
-
sendMessage(String message)
方法- 使用
Session
的getBasicRemote().sendText()
方法发送消息到当前客户端。
- 使用
-
broadcast(String message)
方法- 遍历
clients
集合,将消息广播给所有客户端。
- 遍历
4. 配置 WebSocket 服务端
将 WebSocket 服务注册到 Spring Boot 项目中。
实现代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.websocket.server.ServerEndpointConfig;@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointConfig.Configurator websocketConfigurator() {return new ServerEndpointConfig.Configurator();}
}
步骤说明:
- 配置类使用
@Configuration
注解。 - 将
@ServerEndpoint
标注的类注册为 WebSocket 服务。
5. 创建 WebSocket 客户端
使用 javax.websocket
提供的 API 创建客户端,用于连接服务端并与其通信。
完整代码示例
import javax.websocket.*;
import java.net.URI;@ClientEndpoint
public class WebSocketClient {@OnOpenpublic void onOpen(Session session) {System.out.println("客户端成功连接到服务端");try {session.getBasicRemote().sendText("Hello, WebSocket Server!");} catch (IOException e) {e.printStackTrace();}}@OnMessagepublic void onMessage(String message) {System.out.println("收到服务端消息:" + message);}@OnClosepublic void onClose() {System.out.println("客户端连接已关闭");}@OnErrorpublic void onError(Throwable error) {System.err.println("客户端发生错误:" + error.getMessage());}public static void main(String[] args) {WebSocketContainer container = ContainerProvider.getWebSocketContainer();String uri = "ws://localhost:8080/websocket";try {container.connectToServer(WebSocketClient.class, URI.create(uri));} catch (Exception e) {e.printStackTrace();}}
}
代码详解
-
类注解 @ClientEndpoint
- 指定该类为 WebSocket 客户端。
-
@OnOpen
方法- 在客户端成功连接到服务端时调用。
- 使用
Session
对象的getBasicRemote().sendText()
方法向服务端发送消息。
-
@OnMessage
方法- 在客户端收到服务端消息时调用。
- 参数
message
:服务端发送的消息。
-
@OnClose
方法- 在客户端与服务端断开连接时调用。
-
@OnError
方法- 在通信过程中发生异常时触发。
-
main
方法- 使用
WebSocketContainer
创建客户端连接,连接地址为ws://localhost:8080/websocket
。
- 使用