场景:
手机端发送消息,在大屏同步展示;
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
1. 后台类结构和代码
socket相关java代码:
package com.xxx.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Component
public class WebSocketConfig {
/**
* ServerEndpointExporter 作用
*
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
package com.xxx.websocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
@Component
@RequestMapping(value = "/api/")
public class WebSocketController {
@Autowired
private WebSocketServer webSocketServer;
@GetMapping("/hello/{msg}")
@ResponseBody
public String hello(@PathVariable String msg) {
webSocketServer.sendOneMessage("jiayq", msg);
return "Hello, World!";
}
@PostMapping(value = "/sendMsg")
@ResponseBody
public Object chatDB(@RequestParam String params) {
webSocketServer.sendOneMessage("screen", params);
return null;
}
}
package com.xxx.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.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@Slf4j
@ServerEndpoint("/api/websocket/{userId}")
public class WebSocketServer {
private Session session;
private String userId;
private static CopyOnWriteArraySet<WebSocketServer> webSockets =new CopyOnWriteArraySet<>();
private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();
/**
* 链接成功调用的方法
*/
@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) {
}
}
/**
* 链接关闭调用的方法
*/
@OnClose
public void onClose() {
try {
webSockets.remove(this);
sessionPool.remove(this.userId);
log.info("【websocket消息】连接断开,总数为:"+webSockets.size());
} catch (Exception e) {
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message
* @param
*/
@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());
error.printStackTrace();
}
// 此为广播消息
public void sendAllMessage(String message) {
log.info("【websocket消息】广播消息:"+message);
for(WebSocketServer webSocket : webSockets) {
try {
if(webSocket.session.isOpen()) {
webSocket.session.getAsyncRemote().sendText(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息
public void sendOneMessage(String userId, String message) {
Session session = sessionPool.get(userId);
if (session != null&&session.isOpen()) {
try {
log.info("【websocket消息】 单点消息:"+message);
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息(多人)
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消息】 单点消息:"+message);
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
2. vue前端代码
发送界面:
<template>
<div>
<input type="text" v-model="message" placeholder="输入消息">
<button @click="sendMessage">发送消息</button>
</div>
</template>
<script>
import { sendMsg } from '../../api/chat'
export default {
name: "ChatSocket",
data () {
return {
index: -1,
reconnectCount: 0,
message: ""
}
},
created() {
this.connectWebSocket()
},
methods: {
sendMessage: function() {
sendMsg({ msg: this.message })
}
}
}
</script>
<style scoped>
</style>
接收界面:
<template>
<div>
<div v-for="(item, index) in msg">
<markdown-it-vue class="md-body" :content="item.msg"/>
</div>
</div>
</template>
<script>
import MarkdownItVue from 'markdown-it-vue'
import 'markdown-it-vue/dist/markdown-it-vue.css'
export default {
name: "ChatSocket",
data () {
return {
index: -1,
reconnectCount: 0,
msg: [],
synth: null,
speakMsg: ''
}
},
created() {
this.connectWebSocket()
this.synth = window.speechSynthesis
this.speakMsg = new SpeechSynthesisUtterance()
this.speakMsg.lang = "zh-CN"
//this.speakMsg.volume = '1';
//this.speakMsg.rate = 1.5;
//this.speakMsg.pitch = 1;
},
methods: {
handleStop(){
this.synth.cancel(this.speakMsg);
},
popIframe(){
alert("触发父界面操作")
},
connectWebSocket: function() {
if('WebSocket' in window){
this.socket = new WebSocket("ws://127.0.0.1:30316/robot/api/websocket/screen");
} else{
alert('Not support websocket')
}
//最大尝试链接次数
const maxReconnectAttempts = 10;
const reconnectInterval = 1000; // 1秒
const tryReconnect = () => {
if (this.reconnectCount < maxReconnectAttempts) {
console.log('Reconnecting...');
this.connectWebSocket();
this.reconnectCount++;
} else {
console.log('Exceeded max reconnect attempts, stopping reconnecting.');
}
};
this.socket.addEventListener('open', (event) => {
console.log('WebSocket 连接已建立')
let msg = {
text: "这是发送的txt消息"
}
let json = JSON.stringify(msg)
this.socket.send(json)
})
this.socket.addEventListener('message', (event) => {
//接收到的数据
const receivedData = event.data;
let res = JSON.parse(receivedData)
if(res.msg.trim() == '停止播报'){
this.handleStop()
return;
}
this.msg.push(res)
if(res.type == 'answer'){
this.speakMsg.text = res.msg
this.synth.speak(this.speakMsg);
}
//this.popIframe()
console.log('Received data:', receivedData)
})
// socket.addEventListener('close', (event) => {
this.socket.addEventListener('close', (event) => {
console.log('WebSocket connection closed')
// 在连接关闭时触发重连逻辑
// setTimeout(() => {
// console.log('Reconnecting...')
// this.connectWebSocket()
// }, 1000) // 1秒后重连
// 清除重连计数,防止无限递增
// 在连接关闭后,间隔一段时间进行重连
setTimeout(tryReconnect, reconnectInterval);
})
// socket.addEventListener('error', (event) => {
this.socket.addEventListener('error', (event) => {
console.error('WebSocket error:', event)
// 在错误发生时触发重连逻辑
// setTimeout(() => {
// console.log('Reconnecting...')
// this.connectWebSocket()
// }, 1000) // 1秒后重连
// 在连接错误后,间隔一段时间进行重连
setTimeout(tryReconnect, reconnectInterval);
})
window.addEventListener('beforeunload', (event) => {
// 当 tab 关闭时,关闭 WebSocket 连接
this.socket.close();
});
}
},
components: {
MarkdownItVue
}
}
</script>
<style scoped>
</style>
以上实现了socket定点向userId为screen的用户发送消息