Springboot3.X集成WebSocket完整流程
苗锦洲
创建于:2024-12-16 09:51:15
加拿大
0
6
45
0
https://blog.csdn.net/qq_43437208/article/details/136647914
友情提示:此篇文章大约需要阅读 20分51秒
原文链接:https://blog.csdn.net/qq_43437208/article/details/136647914
WebSocket介绍
WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455,后由 RFC 7936 补充规范。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocketAPI 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输
环境介绍
springboot
pom文件。
<!--spring 版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
</parent>
1
2
3
4
5
6
websocket依赖
<!--websocket-->
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
```
1
2
3
4
5
Vue
"vue": "3.2.45",
"websocket": "^1.0.34"
1
2
后端代码示例
目录结构
配置类
package gzsf.epg.sever.webSocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* webSocket的配置类
* @author
* @date 2023/10/7
*/
@Configuration
public class WebSocketConfig {
```
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
```
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package gzsf.epg.sever.webSocket.websocket;
import jakarta.websocket.Session;
/**
* @author
* @date 2023/10/7
*/
public class WebSocketClient {
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//连接的uri
private String uri;
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
如下是后端发送消息的配置,如需发送文本类的数据需要配置这个
package gzsf.epg.sever.webSocket.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import gzsf.epg.sever.datacheck.vo.ExportVo;
import jakarta.websocket.EncodeException;
import jakarta.websocket.Encoder;
import jakarta.websocket.EndpointConfig;
public class ServerEncoder implements Encoder.Text<ExportVo> {
```
@Override
public void destroy() {
// TODO Auto-generated method stub
// 这里不重要
}
@Override
public void init(EndpointConfig arg0) {
// TODO Auto-generated method stub
// 这里也不重要
}
/*
* encode()方法里的参数和Text<T>里的T一致,如果你是Student,这里就是encode(Student student)
*/
@Override
public String encode(ExportVo responseMessage) throws EncodeException {
try {
JsonMapper jsonMapper = new JsonMapper();
return jsonMapper.writeValueAsString(responseMessage);
} catch ( JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
```
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
服务类
package gzsf.epg.sever.webSocket.service;
import gzsf.epg.sever.datacheck.vo.ExportVo;
import gzsf.epg.sever.webSocket.util.ServerEncoder;
import gzsf.epg.sever.webSocket.websocket.WebSocketClient;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author
* @date 2023/10/7
*/
@ServerEndpoint(value = "/websocket/{userName}", encoders = {ServerEncoder.class})
@Component
public class WebSocketService {
private static final Logger log = LoggerFactory.getLogger(WebSocketService.class);
```
private static ApplicationContext applicationContext;
public static void setApplicationContext(ApplicationContext context) {
applicationContext = context;
}
private static ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收userName
*/
private String userName;
/**
* 客户端与服务端连接成功
*
* @param session
*/
@OnOpen
public void onOpen(Session session, @PathParam("userName") String userName) {
this.session = session;
//根据token获取用户名
this.userName = userName;
WebSocketClient client = new WebSocketClient();
client.setSession(session);
client.setUri(session.getRequestURI().toString());
webSocketMap.put(userName, client);
log.info("连接成功!"+session.getRequestURI().toString());
log.info("连接成功!"+session);
}
/**
* 客户端与服务端连接关闭
*/
@OnClose
public void onClose() {
log.info("连接关闭!");
}
/**
* 客户端与服务端连接异常
*
* @param error
* @param session
*/
@OnError
public void onError(Throwable error, Session session) {
}
/**
* 客户端向服务端发送消息
*
* @param message
* @throws IOException
*/
@OnMessage
public void onMsg(Session session, String message) throws IOException {
log.info("消息测试!");
}
public static void sendVo(String userName, ExportVo vo) {
try {
WebSocketClient webSocketClient = webSocketMap.get(userName);
if (webSocketClient != null) {
webSocketClient.getSession().getBasicRemote().sendObject(vo);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
} catch (EncodeException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 向指定客户端发送消息
*
* @param message
*/
public static void sendMessage(String userName, String message) {
try {
log.info("准备发消息!"+userName);
WebSocketClient webSocketClient = webSocketMap.get(userName);
log.info("连接成功》》》》》》》》》"+webSocketMap);
if (webSocketClient != null) {
log.info("发送的消息是!"+message);
webSocketClient.getSession().getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
log.info("失败");
throw new RuntimeException(e.getMessage());
}
}
```
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
需要注意
修改启动类如下,服务类就能注入,否则在调用service的时候会空指针异常,具体原因可以去了解下 spring 默认管理
package gzsf.epg.sever;
import gzsf.epg.common.core.utils.SpringContextUtils;
import gzsf.epg.common.security.annotation.EnableCustomConfiguration;
import gzsf.epg.common.security.annotation.EnableFeignBeanClients;
import gzsf.epg.sever.webSocket.service.WebSocketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
/**
* @author
* @date 2023/10/19
*/
@EnableCustomConfiguration
@EnableFeignBeanClients
@SpringBootApplication
@Import(SpringContextUtils.class)
public class ServiceApplication {
private static final Logger logger = LoggerFactory.getLogger(ServiceApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(ServiceApplication.class, args);
//解决WebSocket不能注入的问题
WebSocketService.setApplicationContext(run);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
前端代码示例
连接地址里 admin为用户名,因为websocket是单独为每个用户创建长连接,发送的消息也是一对一发送,当然也可以改为一对多,全权在业务怎么用。前端可获取用户名传到后端,如下代码示例是写死的
注意:前端也需下载依赖
下面展示一些 内联代码片。
npm install vue-native-websocket
1
/**
* 创建webSocket连接
*/
function createWS() {
webSocket.value = new WebSocket("ws://127.0.0.1:8080/server/websocket/admin");
//onopen事件监听
webSocket.value.addEventListener('open', e => {
console.log('与服务端连接打开->', e)
}, false)
//onclose事件监听
webSocket.value.addEventListener('close', e => {
console.log('与服务端连接关闭->', e)
}, false)
//onmessage事件监听
webSocket.value.addEventListener('message', e => {
console.log('接收到服务端的消息->', e)
}, false)
//onerror事件监听
webSocket.value.addEventListener('error', e => {
}, false)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
只需在需要开启长连接的时候调用createWS();即可;
简单说明一下执行流程
前端
在需要开启长连接处调用createWS();开启长连接即可,需注意用户名(该用户名就是唯一标识)传递。
后端
前端发起建立长连接请求后,执行WebSocketService类中的onOpen方法,创建client并存放在map集合中以便执行完业务后发送反馈消息。
```
/**
* 客户端与服务端连接成功
*
* @param session
*/
@OnOpen
public void onOpen(Session session, @PathParam("userName") String userName) {
this.session = session;
//根据token获取用户名
this.userName = userName;
WebSocketClient client = new WebSocketClient();
client.setSession(session);
client.setUri(session.getRequestURI().toString());
webSocketMap.put(userName, client);
log.info("连接成功!"+session);
}
```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如何调用
后端接收到前端请求后可新建线程执行业务流程,先返回执行中状态给前端,防止前后端连接超时的报错。
```
@PostMapping("/importDx")
public AjaxResult importDx(@RequestParam("xlsxFile") MultipartFile file) {
this.onCallback(file);
return AjaxResult.success();
}
public void onCallback(MultipartFile file) {
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(() -> {
try {
//服务类代码
} catch (IOException e) {
throw new RuntimeException(e);
}
});
executor.shutdown();
}
```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
业务执行完成后可调用,username为创建websocket的唯一标识。
/**
* 调用sendMessage方法使用
*/
WebSocketService.sendMessage(username, "执行成功");
1
2
3
4
只要是在长连接里面,想做什么全看业务,简单来说干什么都行!至此WebSocket使用结束
————————————————
```
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
```
原文链接:https://blog.csdn.net/qq_43437208/article/details/136647914
评论
已自动恢复阅读位置、日/夜间模式参数