springBoot整合webSocket,超简单入门
webSocket简洁
WebSocket 是一种基于 TCP 协议的全双工通信协议,它允许客户端和服务器之间建立持久的、双向的通信连接。相比传统的 HTTP 请求 - 响应模式,WebSocket 提供了实时、低延迟的数据传输能力。通过 WebSocket,客户端和服务器可以在任意时间点互相发送消息,实现实时更新和即时通信的功能。WebSocket 协议经过了多个浏览器和服务器的支持,成为了现代 Web 应用中常用的通信协议之一。它广泛应用于聊天应用、实时数据更新、多人游戏等场景,为 Web 应用提供了更好的用户体验和更高效的数据传输方式。
1、导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、写配置类
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter,
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3、创建ServerEndpoint 端点
服务端 WebSocket 端点的开发也有 2 种方式。第 1 种是实现规范所提供的各种接口,通过接口定义的回调方法来处理新的连接、客户端消息、连接断开等等事件。另一种方式是使用注解,类似于 Spring 中的 Controller,通过在方法上使用不同的注解来监听不同的 WebSocket 事件,灵活性比较高,推荐使用。
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}") // 接口路径 ws://localhost:8080/websocket/userId;
public class WebSocket {
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 用户ID
*/
private String userId;
// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
// 虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
// 注:底下WebSocket是当前类名
private static final CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
// 用来存在线连接用户信息
private static final ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();
/**
* 链接成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "userId") String userId) {
try {
this.session = session;
this.userId = userId;
webSockets.add(this);
sessionPool.put(userId, session);
log.info("【websocket消息】有新的连接,总数为:" + webSockets.size());
} catch (Exception e) {
log.error("onOpen 报错", e);
}
}
/**
* 链接关闭调用的方法
*/
@OnClose
public void onClose() {
try {
webSockets.remove(this);
sessionPool.remove(this.userId);
log.info("【websocket消息】连接断开,总数为:" + webSockets.size());
} catch (Exception e) {
log.error("onOpen 报错", e);
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message
*/
@OnMessage
public void onMessage(String message) {
log.info("【websocket消息】收到客户端消息:" + message);
}
/**
* 发送错误时的处理
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误,原因:" + error.getMessage());
}
// 此为广播消息
public void sendAllMessage(String message) {
log.info("【websocket消息】广播消息:" + message);
for (WebSocket webSocket : webSockets) {
try {
if (webSocket.session.isOpen()) {
webSocket.session.getAsyncRemote().sendText(message);
}
} catch (Exception e) {
log.error("sendAllMessage 报错", e);
}
}
}
// 此为单点消息
public void sendOneMessage(String userId, String message) {
Session session = sessionPool.get(userId);
if (session != null && session.isOpen()) {
try {
log.info("【websocket消息】 单点消息: 发给用户【{}】:【{}】",userId,message);
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
log.error("sendOneMessage 报错", e);
}
}
}
// 此为单点消息(多人)
public void sendMoreMessage(String[] userIds, String message) {
for (String userId : userIds) {
Session session = sessionPool.get(userId);
if (session != null && session.isOpen()) {
try {
log.info("【websocket消息】 单点消息(多人): 发给用户【{}】:【{}】",userId,message);
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
log.error("sendMoreMessage 报错", e);
}
}
}
}
}
4、写controller用于模仿数据刷新
@RestController
@Slf4j
@RequestMapping("web")
public class WebSocketTestController {
@Resource
private WebSocket webSocket;
// 模仿其他的服务调用webSocket进行数据刷新
@PostMapping("/test")
public Results test(@RequestBody TestDto dto) {
log.info("test 入参 dto:{}",dto);
try {
// 数据返回
JSONObject obj = new JSONObject();
obj.put("audience",Math.random() * 100);
obj.put("msg",dto.getMsg());
// 全体发送 自己去测
// webSocket.sendAllMessage(obj.toJSONString());
if (StringUtils.isNotBlank(dto.getUserId())){
// 单个用户发送 (userId为用户id)
webSocket.sendOneMessage(dto.getUserId(), obj.toJSONString());
}
if (dto.getUserIds() != null && dto.getUserIds().length > 0){
// 多个用户发送 (userIds为多个用户id)
webSocket.sendMoreMessage(dto.getUserIds(), obj.toJSONString());
}
return Results.success();
} catch (Exception e) {
log.error("test 报错",e);
return Results.failed();
}
}
}
// 偷个懒
@Data
class TestDto{
private String userId;
private String[] userIds;
private String msg;
}
5、测试
使用测试网站:WebSocket在线测试工具 (wstool.js.org)
创建三个用户
开启连接
查看日志
使用postman用于测试
5.1、测试只要123收到消息
发送:
{
"userId":"123",
"userIds":[
],
"msg":"are you ok ?"
}
5.2、测试456、789收到消息
发送:
{
"userId":"",
"userIds":[
"456",
"789"
],
"msg":"6666"
}