一、环境准备
1. 依赖配置(Maven)
在 pom.xml 中添加 WebSocket 依赖: xml
<!-- Spring Boot WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- 原生 WebSocket API(备用) -->
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
二、服务端实现(Spring Boot)
1. WebSocket 配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter(); // 自动注册 @ServerEndpoint
}
}
2. WebSocket 服务端类
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@ServerEndpoint("/chat/{username}")
public class ChatWebSocketServer {
// 存储所有会话
private static final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
private static final AtomicInteger onlineCount = new AtomicInteger(0);
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
sessions.put(username, session);
onlineCount.incrementAndGet();
System.out.println("用户 " + username + " 上线,当前在线人数:" + onlineCount);
}
@OnMessage
public void onMessage(String message, Session session, @PathParam("username") String username) {
System.out.println("收到消息:" + message);
// 广播消息给所有用户
broadcast(username + ": " + message);
}
@OnClose
public void onClose(Session session, @PathParam("username") String username) {
sessions.remove(username);
onlineCount.decrementAndGet();
System.out.println("用户 " + username + " 下线,当前在线人数:" + onlineCount);
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
// 广播消息
private void broadcast(String message) {
sessions.values().forEach(session -> {
try {
session.getAsyncRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
三、客户端实现
1. 浏览器端(HTML + JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>WebSocket 测试客户端</title>
</head>
<body>
<input type="text" id="username" placeholder="用户名">
<input type="text" id="message" placeholder="消息内容">
<button onclick="sendMessage()">发送</button>
<div id="output"></div>
<script>
const ws = new WebSocket("ws://localhost:8080/chat/用户昵称");
ws.onmessage = function(event) {
document.getElementById("output").innerHTML += event.data + "<br>";
};
function sendMessage() {
const msg = document.getElementById("message").value;
ws.send(msg);
}
</script>
</body>
</html>
2. Java 客户端(控制台交互)
import javax.websocket.*;
import java.net.URI;
import java.util.Scanner;
@ClientEndpoint
public class WebSocketClient {
private Session session;
public WebSocketClient(URI uri) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, uri);
} catch (Exception e) {
e.printStackTrace();
}
}
@OnOpen
public void onOpen(Session session) {
this.session = session;
System.out.println("已连接到服务器");
}
@OnMessage
public void onMessage(String message) {
System.out.println("收到消息:" + message);
}
public void sendMessage(String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebSocketClient client = new WebSocketClient(URI.create("ws://localhost:8080/chat/TestUser"));
Scanner scanner = new Scanner(System.in);
while (true) {
String msg = scanner.nextLine();
client.sendMessage(msg);
}
}
}
四、运行与测试
1. 启动服务端
运行 Spring Boot 应用,访问 http://localhost:8080。
2. 浏览器客户端
3. 在浏览器中打开 HTML 文件,输入用户名和消息即可发送。 Java 控制台客户端
运行 WebSocketClient 类,输入消息发送到服务端。
五、关键功能说明
1. 路径参数传递
通过 @PathParam("username") 获取 URL 中的参数(如用户昵称)
2. 消息广播
使用 ConcurrentHashMap 存储所有会话,遍历发送消息
3. 异步发送
session.getAsyncRemote().sendText() 避免阻塞主线程
六、注意事项
1. 依赖冲突
确保 javax.websocket-api 与 Spring Boot 版本兼容。
2. 线程安全
使用 ConcurrentHashMap 保证多线程环境下会话操作的原子性
3. 异常处理
在 @OnError 中捕获连接异常,避免服务端崩溃
4. 路径配置
若需携带参数(如认证 Token),需在服务端解析 session.getRequestParameterMap()