在实际开发中,尤其是web开发,我该如何做才可以实现消息或者数据的实时更新呢。
这里我为大家介绍,websocket长连接,它可以简历连接,且创建一个通道,通道中的数据可以实时更新。
废话不多说,接下来我将使用vue+springboot基于websocket来实现一个简单的聊天实现。
vue前端代码,这里主要的功能就是连接后端websocket服务器,然后发送消息。
<script setup>
import { reactive, ref, onMounted, onBeforeUnmount } from 'vue';
const messageCe = ref('');
const receivedMessages = ref([]);
const HEART_CHECK_TIME = 30000;
const data = reactive({
readyLine: 0,
onLine: 0,
outLine: 0,
errLine: 0,
});
const userId = 1;
const wsuri = `ws://localhost:8080/websocket/${userId}`;
let ws = new WebSocket(wsuri);
const heartCheck = createHeartCheck(ws, { userId });
onMounted(() => {
ws.onopen = () => {
ws.send(JSON.stringify({ test: "12345连接无误", toUserId: userId }));
};
ws.onerror = () => {
reconnect();
};
ws.onmessage = (event) => {
handleMessage(event.data);
heartCheck();
};
ws.onclose = () => {
console.log("已经关闭连接");
};
});
onBeforeUnmount(() => {
ws.close();
});
const sendMessage = () => {
if (messageCe.value.trim() === '') return; // 防止发送空消息
const data_mm = JSON.stringify({ message: messageCe.value }); // 将输入消息序列化
ws.send(data_mm);
messageCe.value = ''; // 清空输入框
};
function handleMessage(data) {
try {
const obj = JSON.parse(data);
if (obj.message) {
// 记录所有接收到的消息
receivedMessages.value.push(`${obj.username || '匿名'}: ${obj.message}`);
}
} catch (e) {
console.error('JSON字符串格式错误:', e);
}
}
function reconnect() {
console.log("尝试重新连接...");
setTimeout(() => {
ws = new WebSocket(wsuri);
}, 3000);
}
function createHeartCheck(ws, { userId }) {
let timer = null;
let timeoutTimer = null;
return () => {
clearTimeout(timer);
clearTimeout(timeoutTimer);
timer = setTimeout(() => {
const message = JSON.stringify({ checkHeart: 'ping', toUserId: userId });
ws.send(message);
console.log(`【发送消息】 ${message}`);
timeoutTimer = setTimeout(() => {
ws.close(); // 超时关闭连接
}, HEART_CHECK_TIME);
}, HEART_CHECK_TIME);
};
}
</script>
<template>
<div class="chat-container">
<div class="messages" v-for="(msg, index) in receivedMessages" :key="index">
{{ msg }}
</div>
<div class="input-container">
<input type="text" v-model="messageCe" @keyup.enter="sendMessage" placeholder="输入消息..." />
<button @click="sendMessage">发送</button>
</div>
</div>
</template>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 400px;
width: 300px;
border: 1px solid #ccc;
border-radius: 8px;
overflow: hidden;
background-color: #f9f9f9;
margin: 20px auto;
}
.messages {
flex: 1;
padding: 10px;
overflow-y: auto; /* 允许垂直滚动 */
border-bottom: 1px solid #ddd;
}
.input-container {
display: flex;
padding: 10px;
background-color: #fff;
}
input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
}
button {
padding: 10px 15px;
border: none;
border-radius: 4px;
background-color: #007bff;
color: white;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>
spring boot代码
pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
项目结构
config配置代码
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
testController代码
@RestController
@RequestMapping("/test")
@Api(tags = { "测试" })
public class TestController {
//推送数据接口
@PostMapping("/socket/push/{sid}")
public Map pushToWeb(@PathVariable String sid, String message) {
Map<String,Object> result = new HashMap<>();
try {
WebSocketServer.sendInfo(message, sid);
result.put("code", sid);
result.put("msg", message);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
server也就是websocket的代码,这里是整个项目的核心,这里有相关路径,而且提供每个生命节点的相关解决方法。
package my.websocket.service;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.ArrayList;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
@Service
@ServerEndpoint("/websocket/{sid}")
public class WebSocketServer {
private static int onlineCount = 0;
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
// 用于存储历史消息
private static List<String> historyMessages = new ArrayList<>();
private Session session;
private String sid = "";
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
this.session = session;
webSocketSet.add(this);
this.sid = sid;
addOnlineCount();
try {
// 发送连接成功消息
sendMessage("conn_success");
// 发送历史消息
for (String message : historyMessages) {
sendMessage(message);
}
log.info("有新窗口开始监听:" + sid + ",当前在线人数为:" + getOnlineCount());
} catch (IOException e) {
log.error("websocket IO Exception");
}
}
@OnClose
public void onClose() {
webSocketSet.remove(this);
subOnlineCount();
log.info("释放的sid为:" + sid);
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自窗口" + sid + "的信息:" + message);
// 存储历史消息
historyMessages.add(message);
// 群发消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@OnError
public void one rror(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
log.info("推送消息到窗口" + sid + ",推送内容:" + message);
for (WebSocketServer item : webSocketSet) {
try {
if (sid == null || item.sid.equals(sid)) {
item.sendMessage(message);
}
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
return webSocketSet;
}
}
接下来就是启动前后端的项目了。
然后前端界面长这样
这里可以看到我们连接到了后台
接下来我们就聊聊这里我们做了啥,我们现在是实现前端界面发送消息之后,其他界面可以接收到,并且在我们发送消息后,再打开一个新页面,它可以同步历史消息
这里可以发现,俩个连接都可以实现互相发送消息,且历史数据也会被读出,
并且后台也会接收到消息
这里都是窗口1发的消息的原因是,他们连接的都是同一个窗口,因为我这里只是初步演示,所以在前端就没有多做处理,实际开发中都是以为这个为基本进行开发的,而且细心的人应该也能发现,我在vue代码中绑定了一个死数据id,所以会全是一个窗口。
接下来我们再模拟一下通过后台发送消息,这里我们使用apifox这个软件来实现发送消息。
这里我之前前端代码没有实现,所以我修改了一个函数,代码如下
function handleMessage(data) {
try {
console.log(data);
const obj = JSON.parse(data);
if (obj.message) {
// 记录所有接收到的消息
receivedMessages.value.push(`${obj.username || '匿名'}: ${obj.message}`);
}
} catch (e) {
console.error('JSON字符串格式错误:', e);
receivedMessages.value.push(`系统消息: ${data}`);
}
}
先编写后台api对应的参数
然后发送数据,这里可以看到我们前端接收到了后台发送过来的数据,支持,spring boot整合websocket结束
如有相关疑问,欢迎加入q裙463727795一起探讨编程问题!!!
标签:Websocket,ws,聊天,整合,sid,websocket,message,public,const From: https://blog.csdn.net/zhdbshiai/article/details/143322958