spring中使用websocket
在spring中使用websocket,与前端通讯,实时推送发布消息给前端展示
1. 引入websocket,在pom.xml中加入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 配置WebSocketConfig
@Configuration
public class WebSocketConfig {
/**
* ServerEndpointExporter 作用
*
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. 配置WebSocketServer,定义接收格式
@Slf4j
@ServerEndpoint(value = "/ws/{uid}")
@Component
public class WebSocketServer {
private Session curSession;
private String curUId;
private static ConcurrentHashMap<String, WebSocketServer> websocketMap = new ConcurrentHashMap<>();
private NoticeService noticeService;
private static ApplicationContext applicationContext;
public static void setApplicationContext(ApplicationContext applicationContext) {
WebSocketServer.applicationContext = applicationContext;
}
//实现一个账户页面多开的时候,任然可以接收到消息
@OnOpen
public void onOpen(Session session, @PathParam("uid") Long uid) {
this.curSession = session;
String uidStr = uid.toString();
Integer maxIndex = 1;
for (String key : websocketMap.keySet()) {
if (key.contains(uidStr)) {
//相同的id情况下,接收到新链接的时候,获取最大index
maxIndex = Integer.parseInt(key.split("_")[1]) + 1;
}
}
uidStr = uidStr + "_" + maxIndex;
websocketMap.put(uidStr, this);
this.curUId = uidStr;
noticeService = applicationContext.getBean(NoticeService.class);
PageResult<NoticeReceiveResult> received = noticeService.received(uid);
try {
sendMessage(JSON.toJSONString(received));
} catch (IOException e) {
log.error("ws IO 异常");
}
}
@OnClose
public void onClose() {
if (websocketMap.get(this.curUId) != null) {
websocketMap.remove(this.curUId);
log.info(curUId + "已经关闭");
}
}
@OnMessage
public void onMessage(String message, Session session) {
if (StringUtils.isNoneBlank(message)) {
for (WebSocketServer server : websocketMap.values()) {
try {
server.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送消息
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.curSession.getBasicRemote().sendText(message);
}
/**
* uid==null,发送给所有,否则发送给指定uid
*
* @param message
* @param uid
* @throws IOException
*/
public static void sendInfo(String message, @PathParam("uid") Long uid) throws IOException {
for (WebSocketServer server : websocketMap.values()) {
try {
if (uid == null) {
server.sendMessage(message);
} else if (server.curUId.equals(uid)) {
server.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
}
}
4. 配置启动项,否则websocket服务类中,定义的其他类将为null,因为没有上下文,无法拿到bean。此项目启动项的类为SpringApplication, WebSocketServer.setApplicationContext(context)
@SpringBootApplication
public class SpringTestApplication {
private static final Log log = Log.get();
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringTestApplication.class, args);
log.info(">>> " + SpringTestApplication.class.getSimpleName() + " is success!");
WebSocketServer.setApplicationContext(context);
}
}
5. 前端使用。前端使用vue,对于其他框架或者纯js也是通用的。
5.1 在mounted时,进行ws连接
mounted() {
this.openSocket()
//若无法连接,将使用定时器,反复请求连接
const timer = setInterval(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) {
this.openSocket()
} else if (this.socket && this.socket.readyState === 1) {
clearInterval(timer)
}
}, 500)
}
5.2 使用
openSocket() {
if (typeof WebSocket === 'undefined') {
console.log('浏览器不支持websocket')
} else {
console.log('浏览器支持websocket')
var wsUrl = 'http:localhost:8888/ws/' + this.currentUser.id
wsUrl = wsUrl.replace('https', 'ws').replace('http', 'ws')
this.socket = new WebSocket(wsUrl)
this.socket.onopen = function() {
console.log('ws已经连接')
}
var that = this //
this.socket.onmessage = function(msg) {
// 1.初始化数据
that.InitTipData(JSON.parse(msg.data));//调用InitTipData,初始化数据
console.log('ws接收消息:' + msg.data)
}
this.socket.onclose = function() {
console.log('ws关闭了')
}
this.socket.onerror = function() {
console.log('ws 发送错误')
}
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
try {
this.socket.close()
} catch (e) {
console.log('ws error:' + e)
}
}
}
},
关于websocket鉴权以及其他常见问题,可以查考网上其他,例如:
https://blog.csdn.net/qq_38531706/article/details/118026034
https://blog.csdn.net/weixin_39263573/article/details/126276427?spm=1001.2014.3001.5501
其他问题:
https://blog.csdn.net/qq_41695498/article/details/104149455
https://blog.csdn.net/kshon/article/details/102522954
wss
https://blog.csdn.net/BHSZZY/article/details/123229338
https://blog.csdn.net/carrie7788/article/details/127517877
https://blog.csdn.net/KeepStruggling/article/details/105543449
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」