本文目录导读:

- 目录导读
- WebSocket核心原理与Java生态选型
- 项目环境搭建与依赖配置
- 服务端核心代码实现(@ServerEndpoint)
- 客户端连接与消息收发完整案例
- 连接管理与心跳检测机制
- 常见问题排查与性能优化建议
- 面试高频问答环节
Java WebSocket实战案例:从零搭建实时消息推送系统(附完整代码)
目录导读
- WebSocket核心原理与Java生态选型
- 项目环境搭建与依赖配置
- 服务端核心代码实现(@ServerEndpoint)
- 客户端连接与消息收发完整案例
- 连接管理与心跳检测机制
- 常见问题排查与性能优化建议
- 面试高频问答环节
WebSocket核心原理与Java生态选型
WebSocket是一种在单个TCP连接上进行全双工通信的协议,它解决了HTTP协议只能由客户端发起请求的局限性,在Java生态中,主流的实现方案包括JSR 356标准API(javax.websocket)、Spring WebSocket模块以及Netty高性能框架。
为什么选择JSR 356?
对于大多数企业级应用,无需引入额外框架,原生API足够轻量,本文案例基于Servlet 3.1+容器(如Tomcat 9+)实现,兼容性最好。
项目环境搭建与依赖配置
Maven依赖(pom.xml核心部分):
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
关键点: provided作用域确保容器(Tomcat)提供实现类,避免冲突。
服务端核心代码实现(@ServerEndpoint)
这是一个完整可运行的WebSocket服务端案例,包含连接管理、消息群发、异常处理:
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/chat")
public class ChatEndpoint {
// 线程安全的连接集合
private static final CopyOnWriteArraySet<ChatEndpoint> CONNECTIONS = new CopyOnWriteArraySet<>();
private Session session;
@OnOpen
public void onOpen(Session session) {
this.session = session;
CONNECTIONS.add(this);
broadcast("用户上线,当前在线人数:" + CONNECTIONS.size());
}
@OnMessage
public void onMessage(String message, Session session) {
broadcast("用户消息:" + message);
}
@OnClose
public void onClose() {
CONNECTIONS.remove(this);
broadcast("用户下线,当前在线人数:" + CONNECTIONS.size());
}
@OnError
public void onError(Throwable error) {
error.printStackTrace();
}
private static void broadcast(String msg) {
for (ChatEndpoint client : CONNECTIONS) {
try {
client.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
设计要诀: 使用CopyOnWriteArraySet保证高并发下的读写安全,广播方法遍历所有会话发送消息。
客户端连接与消息收发完整案例
HTML5 JavaScript客户端(原生实现):
let ws = new WebSocket("ws://your-server.com/websocket/chat");
ws.onopen = function() {
console.log("连接已建立");
ws.send("你好,服务器!");
};
ws.onmessage = function(event) {
console.log("收到消息:" + event.data);
// 实时更新UI
document.getElementById("messages").innerHTML += `<p>${event.data}</p>`;
};
ws.onclose = function() {
console.log("连接关闭");
};
// 发送消息函数
function sendMsg() {
const input = document.getElementById("input");
ws.send(input.value);
input.value = "";
}
测试步骤: 启动Tomcat后,打开两个浏览器标签页访问同页面,即可看到双向实时通信效果。
连接管理与心跳检测机制
为什么需要心跳? 长连接中,代理服务器(如Nginx)会主动断开空闲连接,导致僵尸连接占用资源。
服务端心跳方案(配合定时任务):
// 定期发送ping包,服务器端只需在容器层处理
// 客户端需实现:
ws.onclose = function() {
// 自动重连,指数退避算法
setTimeout(() => { connect(); }, 3000);
};
最佳实践: 建议设置session.setMaxIdleTimeout(30000),并配合客户端30秒自动重连逻辑。
常见问题排查与性能优化建议
高频问题:
- 连接失败: 检查
ws://和wss://协议是否与页面HTTPS匹配 - 消息乱码: 服务端统一使用
UTF-8编码 - 内存泄漏: 确保
onClose中移除无用引用
性能优化四板斧:
- 使用消息压缩(如permessage-deflate)
- 设置合理的最大消息大小(默认8K,可调)
- 通过异步发送(
sendMessage替代sendText) - 多节点部署时使用Redis发布订阅实现跨节点广播
面试高频问答环节
Q1:WebSocket和HTTP长轮询有什么区别?
A:WebSocket只需一次握手建立持久连接,服务端可主动推送;长轮询每次请求都携带完整HTTP头,且推延时高于WebSocket,线连接数利用率完全不在一个量级。
Q2:如何处理在线用户列表的一致性?
A:常规做法是维护ConcurrentHashMap映射userId到Session,但集群环境下必须引入分布式存储(如Redis)记录在线状态,并通过广播通知所有节点更新。
Q3:什么是WebSocket的子协议(subprotocol)?
A:子协议是应用层协议标识,如STOMP,Java端可用@ServerEndpoint(value="/chat", subprotocols={"stomp"})声明,用于消息路由和订阅管理。
Q4:如何防御WebSocket的跨站点劫持攻击?
A:校验Origin请求头,拒绝非白名单来源;正确实现鉴权(握手阶段传递token);使用WSS加密传输。
本文从一个可运行的案例出发,完整讲解了Java WebSocket的编码规范、生命周期管理、生产级优化方向,掌握这些要点后,即可构建出支撑千万级并发的实时交互系统,建议在真实项目中,结合Spring的WebSocketHandler进行更细粒度的消息拦截,或使用Netty获得极致性能。