Spring框架WebSocket
在一个项目中若要实现实时通信,那么websocket必然是首选,如果还使用传统的http请求,无效请求多,响应时间久,服务器压力大,很难适应现实开发需求,而WebSocket则可以完美解决这些问题。
WebSocket的优点
- 实现双向通信: WebSocket支持服务器与客户端之间的全双向通信,双方可以随时主动发送消息,实时接收。
- 低延迟: 连接建立后,WebSocket保持长连接,消息传输无需重复建立HTTP连接,减少了开销。
- 传输效率高: WebSocket协议的头部信息较小,相比于HTTP请求的复杂头部,数据传输效率更高。
- 状态保持: WebSocket连接是持久的,服务器可以维护客户端状态(如会话、用户信息),无需每次请求都携带信息。
- 服务器推送: 服务器主动向客户端推送消息,无需客户端轮询。
常见的WebSocket分为两种使用方法
- Spring WebSocket,基于Spring框架提供的原生WebSocket支持,适合SpringBoot项目。
- JSR-356,基于Java EE的WebSocket标准API,更适合非Spring或混合技术栈项目。
这篇文章要讲的便是第一种WebSocket的基本的使用方式:Spring WebSocket。
Spring WebSocket
一、引入依赖
- 首先创建一个Maven,SpringBoot项目。
- 打开pom文件,添加如下Spring Web和Spring WebSocket依赖。
- 刷新maven项目,确保依赖引入成功。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
二、配置WebSocket
我们需要配置SpringBoot的WebSocket支持,定义WebSocket的端点(endpoint)。 定义WebSocket端点指的是创建一个特定URL,客户端可以通过该URL与服务器建立通信。
- 创建一个SpringBoot配置类,我这里取名叫WebSocketConfig,需要实现WebSocketConfigurer接口。
- 在类上加EnableWebSocket注解,启用Spring的WebSocket支持。
- 重写接口的registerWebSocketHandlers方法,往registry中注册一个WebSocket处理类,添加端点,这里的"chat"就是定义的端点。
- 上述注册的WebSocket处理类目前还未定义,示例代码中即为ChatWebSocketHandler类,后面会创建该类。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
regisry.addHandler(new ChatWebSocketHandler(), "/chat")
.setAllowedOrigins("*"); //运行跨域访问,前后端分离时使用
}
}
三、创建WebSocket处理类
- 创建处理类,我这里叫ChatWebSocketHandler,这个类可以根据实际需求来定义。
- 如果只需要文本数据的传输那么继承TextWebSocketHandler。
- 如果需要传输图片、文件、音视频等二进制数据则继承BinaryWebSocketHandler。
- 如果需要同时处理文本和二进制消息则继承AbstractWebSocketHandler,更加灵活。
此处我选择继承AbstractWebSocketHandler,因为最灵活,代码量和前两个相比也几乎没有增加。
- 定义一个hash用于存储所有活跃的客户端会话。
public class ChatWebSocketHandler extends AbstractWebSocketHandler {
private final Set<WebSocketSession> sessions = new HashSet<>();
}
- 自定义广播给所有在线客户端的broadcastMessage方法,后续重写的方法中会调用
private void broadcastMessage(String message) throws IOException {
TextMessage textMessage = new TextMessage(message);
for (WebSocketSession s : sessions) {
if (s.isOpen()) {
s.sendMessage(textMessage);
}
}
}
- 重写afterConnectionEstablished方法,客户端连接时触发。
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
session.sendMessage(new TextMessage("欢迎来聊天!"));
broadcastMessage("一位新用户加入聊天!");
}
- 重写handleTextMessage方法,处理某个客户端发给服务器的消息,广播给相应的在线客户端。
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
broadcastMessage("用户:" + payload);
}
- 重写handleBinaryMessage,此处不做演示。
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
//不做演示,需要自定义
}
- 重写afterConnectionClosed方法,当客户端退出连接时触发。
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
broadcastMessage("系统:有一位用户退出聊天!");
}
四、前端编写
- 创建一个简单的html页面,让用户可以连接WebSocket、发送消息并显示聊天内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Chat</title>
<style>
#chat-box {
width: 300px;
height: 400px;
border: 1px solid #ccc;
overflow-y: scroll;
margin-bottom: 10px;
padding: 10px;
}
#message-input {
width: 250px;
}
</style>
</head>
<body>
<h2>Simple Chat</h2>
<div id="chat-box"></div>
<input type="text" id="message-input" placeholder="Type a message...">
<button onclick="sendMessage()">Send</button>
<script>
const ws = new WebSocket("ws://localhost:8080/chat");
ws.onopen = function() {
appendMessage("Connected to chat server!");
};
ws.onmessage = function(event) {
appendMessage(event.data);
};
ws.onclose = function() {
appendMessage("Disconnected from chat server!");
};
function sendMessage() {
const input = document.getElementById("message-input");
const message = input.value;
if (message) {
ws.send(message);
input.value = "";
}
}
function appendMessage(message) {
const chatBox = document.getElementById("chat-box");
const p = document.createElement("p");
p.textContent = message;
chatBox.appendChild(p);
chatBox.scrollTop = chatBox.scrollHeight; // 自动滚动到底部
}
</script>
</body>
</html>
- HTML:一个聊天框(chat-box)、输入框(message-input)、按钮
- JavaScript:
- 创建一个WebSocket,根据之前后端定义的端点进行连接
- ws.onopen: 连接成功时逻辑
- ws.onmessage: 服务器往客户端发送消息时的逻辑(服务器->客户端)
- ws.onclose: 连接关闭时逻辑
- sendMessage: 发送输入框中的消息(客户端->服务器)
- appendMessage: 将消息追加到聊天框中展示
五、运行测试
- 启动SpringBoot项目
- 根据配置的端口,如果是8080,访问 http://localhost:8080 ,进入聊天界面。
- 打开多个浏览器(或者隐身窗口),访问 http://localhost:8080 。
- 在一个窗口发送消息,查看其他窗口是否实时接收到消息。
- 关闭窗口应该收到其他用户离开的通知。
扩展
以上教程比较基础,实际开发更加复杂,比如当前项目没有用户信息区分,没有时间区分,如果需要扩展,需要自定义Message类,包含用户及消息相关信息,然后序列化对象后通过TextMessage类发送消息到前端,在进行数据渲染。
- 用户名: 消息上展示用户名
- 私聊: 通过WebSocket消息携带目标用户ID,实现一对一聊天
- 消息持久化: 将消息保存到数据库(如MySQL或MongoDB)