WebSocket 核心概念
在开始编码前,理解几个基本概念很重要:

- WebSocket 协议:一种在单个 TCP 连接上进行全双工通信的协议,它允许服务器主动向客户端推送数据,解决了传统 HTTP 协议只能由客户端发起请求的局限性。
- 握手:WebSocket 连接的建立过程,客户端发送一个特殊的 HTTP 请求,包含
Upgrade: websocket和Connection: Upgrade头,服务器如果支持,会返回一个101 Switching Protocols响应,此后连接就升级为 WebSocket 连接,双方就可以自由地双向传输数据了。 - 会话:一个
Session对象代表一个活跃的 WebSocket 连接,它包含了连接信息、允许你发送消息、关闭连接等。 - 端点:服务器端处理 WebSocket 逻辑的类,通常是一个 Java 类,使用注解来定义其行为。
主流 Java WebSocket 库
Java 生态中有几个成熟的 WebSocket 库,各有特点:
-
Jakarta WebSocket (JSR-356 / Java EE / Jakarta EE)
- 描述:这是 Java 官方的 WebSocket API 标准,如果你使用的是 Java EE (如 WildFly, Payara) 或 Jakarta EE (如 WildFly, Tomcat 10+) 应用服务器,它通常是内置支持的。
- 优点:标准化,代码可移植性好,与 Jakarta EE/Jakarta EE 容器集成紧密。
- 缺点:在独立的 Java 应用(如 Spring Boot)中需要额外引入依赖。
- 注解:
@ServerEndpoint,@OnOpen,@OnClose,@OnMessage,@OnError。
-
Spring Framework WebSocket
- 描述:Spring 框架提供的 WebSocket 支持,与 Spring MVC 和 Spring Boot 深度集成。
- 优点:功能强大,配置灵活,与 Spring 生态无缝集成(如 Security, STOMP 消息代理),非常适合构建复杂的、企业级的应用。
- 缺点:学习曲线相对陡峭,概念比标准 API 更多(如
WebSocketHandler,SockJS,STOMP)。 - 使用方式:通常通过
@Configuration类和@EnableWebSocket注解来配置。
-
Netty
(图片来源网络,侵删)- 描述:一个异步事件驱动的网络应用框架,性能极高,你可以用它来构建自定义的 WebSocket 服务器。
- 优点:性能卓越,功能极其灵活,可以构建定制化的网络协议。
- 缺点:API 相对底层,使用复杂,不适合快速开发。
选择建议:
- 快速入门 & 简单应用:推荐 Jakarta WebSocket (JSR-356),注解清晰直观。
- Spring Boot 项目:强烈推荐 Spring WebSocket,因为它能最好地融入 Spring 生态。
- 高性能 & 定制化:考虑 Netty。
本指南将重点讲解最常用的 Jakarta WebSocket (JSR-356) 和 Spring WebSocket 两种方式。
实现方式一:使用 Jakarta WebSocket (JSR-356)
这种方式非常直接,适合独立的 Java 项目或运行在支持该标准的 Web 容器中。
步骤 1: 添加依赖
如果你使用 Maven (pom.xml):

<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>10.0.0</version> <!-- 使用与你的服务器/环境匹配的版本 -->
<scope>provided</scope> <!-- 如果部署在 Tomcat/WildFly 等容器中,通常不需要打包进去 -->
</dependency>
如果你使用 Gradle (build.gradle):
implementation 'jakarta.platform:jakarta.jakartaee-web-api:10.0.0' // providedCompile
注意:如果你想在 Spring Boot 中使用,并且不想依赖 provided 的容器 API,可以使用 jakarta.websocket-api 这个依赖。
步骤 2: 创建 WebSocket 端点类
创建一个 Java 类,并使用 @ServerEndpoint 注解来定义 WebSocket 端点。
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ServerEndpoint("/websocket/chat") // 定义 WebSocket 的访问路径
public class ChatEndpoint {
// 使用一个静态的 Set 来存储所有活跃的会话,实现广播功能
private static final Set<Session> chatroomUsers = Collections.synchronizedSet(new HashSet<>());
// 当新的 WebSocket 连接建立时被调用
@OnOpen
public void onOpen(Session session) {
chatroomUsers.add(session);
System.out.println("新连接加入: " + session.getId());
broadcast("用户 " + session.getId() + " 已加入聊天室,当前在线人数: " + chatroomUsers.size());
}
// 当收到客户端消息时被调用
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自 " + session.getId() + " 的消息: " + message);
// 广播消息给所有用户
broadcast("用户 " + session.getId() + ": " + message);
}
// 当连接关闭时被调用
@OnClose
public void onClose(Session session) {
chatroomUsers.remove(session);
System.out.println("连接关闭: " + session.getId());
broadcast("用户 " + session.getId() + " 已离开聊天室,当前在线人数: " + chatroomUsers.size());
}
// 当连接发生错误时被调用
@OnError
public void onError(Throwable error, Session session) {
System.out.println("连接发生错误: " + session.getId());
error.printStackTrace();
chatroomUsers.remove(session);
}
// 广播消息的辅助方法
private void broadcast(String message) {
chatroomUsers.forEach(session -> {
try {
// getBasicRemote() 是同步的,对于大量用户可能性能不佳
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
// 如果发送失败,可能连接已断开,从集合中移除
chatroomUsers.remove(session);
}
});
}
}
步骤 3: 部署到服务器
将你的应用打包成 WAR 文件,然后部署到支持 WebSocket 的服务器上,如 Tomcat 9+ 或 WildFly。
实现方式二:使用 Spring WebSocket
这种方式在 Spring Boot 项目中非常流行,提供了更高级的抽象和集成。
步骤 1: 添加依赖
对于 Spring Boot 3.x,spring-boot-starter-web 通常已经包含了所需的核心依赖,如果需要明确添加 WebSocket 支持:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
步骤 2: 配置 WebSocket
创建一个配置类来注册和启用 WebSocket。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册一个自定义的 WebSocket 处理器
// "/websocket/chat" 是 WebSocket 的访问路径
// 允许所有来源的连接,生产环境需要设置 allowedOrigins
registry.addHandler(new MyWebSocketHandler(), "/websocket/chat")
.setAllowedOrigins("*"); // 允许跨域
}
}
步骤 3: 创建 WebSocket 处理器
这个类继承自 TextWebSocketHandler,并重写核心方法。
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
private static final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>());
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
System.out.println("新连接建立: " + session.getId());
// 广播新用户加入的消息
broadcast("用户 " + session.getId() + " 已加入聊天室,当前在线人数: " + sessions.size());
}
@Override
protected void handleTextMessage 