SpringBoot集成WebSocket
- 一、简介
- 二、功能
- 三、依赖配置
- 四、开启WebSocket支持
- 五、服务端处理WebSocket类
- 六、调用方式
- 七、Nginx配置
- 八、异常
一、简介
1、WebSocket的基本概念
WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许服务端主动向客户端推送数据,同时也允许客户端向服务器发送数据。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
2、WebSocket的工作原理
握手过程:客户端向服务器发送一个HTTP请求,请求建立WebSocket连接。服务器收到请求后,会返回一个HTTP响应,响应中包含一个101 Switching Protocols状态码,表示同意建立WebSocket连接。
数据传输:一旦WebSocket连接建立成功,服务器和客户端就可以通过这条TCP连接进行实时、双向的数据传输。数据格式可以是JSON、二进制或文本。
3、WebSocket的实现
实现WebSocket通信通常需要使用JavaScript和后端语言(如、Python、Java等)。在客户端,可以使用JavaScript中的WebSocket API来创建和管理WebSocket连接;在服务器端,则需要使用相应的后端语言来监听WebSocket连接、处理客户端的请求和发送数据给客户端。
二、功能
1、WebSocket的特点
1.实时性强:由于WebSocket是全双工的通信协议,服务器可以主动向客户端推送数据,因此实时性很强。
2.较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。
3.支持二进制数据:WebSocket定义了二进制帧,因此可以更轻松地处理二进制内容。
4.跨域支持:WebSocket协议可以跨域使用,允许不同源的客户端与服务器进行通信。
2、WebSocket的应用场景
1.即时通讯:如聊天应用、在线客服系统等,用户可以实时地发送和接收消息。
2.实时数据展示:如实时股票行情、实时天气更新等,通过WebSocket可以实时地推送数据给前端。
3.多人游戏:WebSocket可以实现多人在线游戏,玩家可以实时地进行交互、通信。
4.实时协作:如实时协同编辑器,多个用户可以同时编辑一个文档,并实时地看到其他用户的操作。
5.数据监控:WebSocket可以用于实时监控系统的运行状态、日志更新等,便于及时发现和解决问题。
三、依赖配置
<dependency>
<groupId></groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
四、开启WebSocket支持
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类,用于开启WebSocket支持
* 通过此类配置,Spring Boot应用程序可以自动注册WebSocket端点,使得使用JSR 356 (Java API for WebSocket) 定义的端点能够被自动发现和使用。
*/
@Configuration
public class WebSocketConfig {
/**
* 定义一个Bean,类型为ServerEndpointExporter,用于自动注册实现了WebSocket规范(JSR 356)的@ServerEndpoint注解声明的WebSocket端点。
*
* <p>
* 当在Spring Boot应用程序中使用@ServerEndpoint注解声明WebSocket端点时,需要此Bean来确保端点能够被正确地注册和暴露。
* </p>
*
* @return ServerEndpointExporter的实例,用于自动注册WebSocket端点
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
五、服务端处理WebSocket类
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*WebSocket服务器端点类,用于处理WebSocket连接的建立、消息接收、关闭等事件。
*/
@Slf4j
@ServerEndpoint(value = "/websocket/{Id}")// 声明WebSocket服务器端点路径,其中{Id}为路径参数(自行配置)
@Component
public class WebSocketServer {
/**
* 用于存储Id到session的映射
*/
private static final Map<String, Session> SESSIONS_BY_ORDER_ID = new ConcurrentHashMap<>();
/**
* WebSocket连接打开时调用。
*
* @param session WebSocket会话对象
*/
@OnOpen
public void onOpen(Session session, @PathParam("Id") String Id) {
// 假设Id不为空且有效
if (Id != null && !Id.isEmpty()) {
SESSIONS_BY_ORDER_ID.put(Id, session);
}
log.debug("成功建立连接,ID:{}", Id);
}
/**
* WebSocket连接关闭时调用。
*
* @param session WebSocket会话对象
*/
@OnClose
public void onClose(Session session) {
// 从Id到session的映射中移除当前会话
SESSIONS_BY_ORDER_ID.entrySet().removeIf(entry -> entry.getValue().equals(session));
log.debug("成功关闭连接");
}
/**
* 接收到WebSocket消息时调用。
*
* @param message 接收到的文本消息
* @param session WebSocket会话对象
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("接收到的消息: {},来自会话ID:{}", message, session.getId());
}
/**
* WebSocket连接发生错误时调用。
*
* @param session WebSocket会话对象
* @param error 发生的异常
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("websocket发生错误,会话ID:{}", session.getId(), error);
}
/**
* 根据Id向对应的WebSocket会话发送消息。
*
* @param Id ID
*/
public void sendMessageById(String Id) {
Session session = SESSIONS_BY_ORDER_ID.get(Id);
if (session != null && session.isOpen()) {
try {
session.getBasicRemote().sendText(Id);
} catch (IOException e) {
log.error("根据ID发送消息时发生错误:{}", Id, e);
}
} else {
log.warn("找不到与ID匹配的会话:{}", Id);
}
}
/**
* 向所有与指定Id关联的WebSocket会话发送消息。
*
* @param Id ID
* @param message 要发送的文本消息
*/
public void fanoutMessageById(String Id, String message) {
SESSIONS_BY_ORDER_ID.entrySet().stream()
.filter(entry -> Id.equals(entry.getKey()))
.forEach(entry -> {
Session session = entry.getValue();
if (session.isOpen()) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.warn("向ID:{}的会话发送消息时发生异常", Id, e);
}
}
});
}
}
到此为止,我们的WebSocket服务端已经配置完毕。
六、调用方式
根据Id向对应的WebSocket会话发送消息。
WebSocketServer.sendMessageByOrderId(latestrId.get());
七、Nginx配置
#websocket wss请求
location /websocket/ {
# 将请求代理到指定的域名和端口上。
proxy_pass https://域名:端口;
# 设置代理的HTTP版本为1.1,因为WebSocket连接需要HTTP/1.1的Upgrade和Connection头部
proxy_http_version 1.1;
# 这对于WebSocket连接是必须的,因为WebSocket是通过HTTP的Upgrade机制来“升级”到WebSocket协议的
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# 将客户端请求的Host头部传递给后端服务器
# 这对于后端服务器基于虚拟主机(Virtual Host)的配置来路由请求是必要的
proxy_set_header Host $host;
# 如果请求中包含Upgrade头部,则绕过缓存
# 这对于WebSocket连接是必要的,因为WebSocket连接是长连接,不适合被缓存
proxy_cache_bypass $http_upgrade;
# 如果请求中包含Upgrade头部,则不缓存响应
# 类似于proxy_cache_bypass,这确保WebSocket响应不会被缓存
proxy_no_cache $http_upgrade;
# 设置代理读取超时时间为300秒(5分钟)
proxy_read_timeout 300;
}
#websocket ws请求
location /websocket/ {
# 将请求代理到指定的IP地址和端口上。
proxy_pass http://IP地址:端口;
# 设置代理的HTTP版本为1.1,因为WebSocket连接需要HTTP/1.1的Upgrade和Connection头部
proxy_http_version 1.1;
# 这对于WebSocket连接是必须的,因为WebSocket是通过HTTP的Upgrade机制来“升级”到WebSocket协议的
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# 将客户端请求的Host头部传递给后端服务器
# 这对于后端服务器基于虚拟主机(Virtual Host)的配置来路由请求是必要的
proxy_set_header Host $host;
# 如果请求中包含Upgrade头部,则绕过缓存
# 这对于WebSocket连接是必要的,因为WebSocket连接是长连接,不适合被缓存
proxy_cache_bypass $http_upgrade;
# 如果请求中包含Upgrade头部,则不缓存响应
# 类似于proxy_cache_bypass,这确保WebSocket响应不会被缓存
proxy_no_cache $http_upgrade;
# 设置代理读取超时时间为300秒(5分钟)
proxy_read_timeout 300;
}
八、异常
Websocket异常接收不到请求 添加@EnableWebSocket注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
/**
* 启动程序
*
*/
@EnableWebSocket
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
添加 @EnableWebSocket 注解的目的是启用 Spring Boot 中的 WebSocket 功能。
通过添加这个注解,Spring Boot 会自动配置和启用与 WebSocket 相关的基础设施和处理机制,以便能够处理 WebSocket 连接、接收和发送消息等操作。如果不添加这个注解,可能会导致无法正常接收 WebSocket 请求,因为相关的配置和功能没有被启用。