#一、创建服务,如图所示:
#二、添加依赖,示例如下:
<!-- Spring WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
#三、创建websocket服务配置类,示例如下:
import com.ruoyi.websocket.websocket.DriverWebSocketHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket // 启用WebSocket支持
public class WebSocketConfig implements WebSocketConfigurer {
private final DriverWebSocketHandler webSocketHandler;
// 注入自定义的消息处理器
public WebSocketConfig(DriverWebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册WebSocket端点,允许前端通过ws://localhost:8080/ws连接
registry.addHandler(webSocketHandler, "/ws")
.setAllowedOrigins("*"); // 允许跨域(生产环境需指定具体域名)
}
}
#四、创建consts,示例如下:
public class MessageConst {
//心跳信息
public static final Integer UP_NEART_BEAT =0;
//上传司机信息
public static final Integer UP_DRIVER_INFO=1;
//上传车辆实时位置信息
public static final Integer UP_CAR_POSITION=2;
//上传车辆信息
public static final Integer UP_CAR_INFO=3;
//上传车辆状态信息
public static final Integer UP_CAR_STATUS=4;
//上传车辆历史轨迹信息
public static final Integer UP_HISTORY_TTRACK=5;
//上传车辆实时轨迹信息
public static final Integer UP_REAL_TRACK=6;
//上传车辆实时位置信息
public static final Integer UP_REAL_POSITION=7;
//上传围栏报警信息
public static final Integer UP_FENCE_WARN=8;
}
#五、创建Vo,示例如下:
import lombok.Data;
import org.springframework.web.socket.WebSocketSession;
@Data
public class DriverInfoVo {
private Integer driverId;
private String driverName;
private WebSocketSession session;
}
#六、回调函数,示例如下:
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class DriverWebSocketHandler extends TextWebSocketHandler {
// 存储所有上报信息的司机信息(线程安全)
// key = driverId
// value = DriverInfoVo
private static final ConcurrentHashMap<String, WebSocketSession> sessionsMap = new ConcurrentHashMap<>();
// 存储所有活跃的WebSocket会话(线程安全)
private static final Set<WebSocketSession> sessions =
Collections.synchronizedSet(new HashSet<>());
// 客户端连接成功时触发
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
System.out.println("新客户端连接,当前在线数:" + sessions.size());
String id = session.getId();
System.out.println("id:" + id);
session.sendMessage(new TextMessage("连接成功!"));
}
// 接收客户端消息时触发
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String clientMessage = message.getPayload();
System.out.println("收到消息:" + clientMessage);
// 示例:向所有客户端广播消息
broadcast("服务器收到:" + clientMessage);
}
// 客户端断开连接时触发
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
System.out.println("客户端断开,当前在线数:" + sessions.size());
}
// 广播消息给所有在线客户端
private void broadcast(String message) throws IOException {
for (WebSocketSession session : sessions) {
if (session.isOpen()) { // 确保连接未关闭
session.sendMessage(new TextMessage(message));
}
}
}
}
#七、创建前端Test.vue文件,示例如下:
注意:这个端口是你后端的端口:
前端页面:
<template>
<div class="websocket-client">
<h3>WebSocket 客户端</h3>
<div class="connection-status">
连接状态:
<span :class="isConnected ? 'connected' : 'disconnected'">
{{ isConnected ? "已连接" : "未连接" }}
</span>
</div>
<div class="message-controls">
<input
type="text"
v-model="messageInput"
placeholder="输入消息..."
:disabled="!isConnected"
/>
<button @click="sendMessage" :disabled="!isConnected || !messageInput">
发送消息
</button>
<button @click="isConnected ? closeConnection() : connect()">
{{ isConnected ? "断开连接" : "连接服务器" }}
</button>
</div>
<div class="message-history">
<h4>消息历史:</h4>
<div
v-for="(msg, index) in messageHistory"
:key="index"
class="message-item"
>
<span :class="msg.isSent ? 'sent' : 'received'">
{{ msg.isSent ? "我" : "服务器" }}: {{ msg.content }}
</span>
<span class="time">{{ msg.timestamp }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from "vue";
// 定义消息类型接口
interface WebSocketMessage {
content: string;
isSent: boolean;
timestamp: string;
}
// WebSocket配置
const WS_URL = ref("ws://localhost:9305/ws"); // 后端WebSocket地址
const isConnected = ref(false);
const webSocket = ref<WebSocket | null>(null);
const messageInput = ref("");
const messageHistory = ref<WebSocketMessage[]>([]);
// 格式化时间
const formatTime = () => {
const date = new Date();
return date.toLocaleTimeString();
};
// 连接WebSocket
const connect = () => {
if (isConnected.value) return;
try {
webSocket.value = new WebSocket(WS_URL.value);
// 连接成功
webSocket.value.onopen = () => {
console.log("WebSocket连接成功");
isConnected.value = true;
addMessage("连接服务器成功", false);
};
// 接收消息
webSocket.value.onmessage = (event) => {
console.log("收到消息:", event.data);
addMessage(event.data, false);
};
// 连接关闭
webSocket.value.onclose = (event) => {
console.log(`WebSocket关闭: ${event.code} ${event.reason}`);
isConnected.value = false;
addMessage(`连接已关闭: ${event.reason}`, false);
webSocket.value = null;
};
// 连接错误
webSocket.value.onerror = (error) => {
console.error("WebSocket错误:", error);
addMessage("连接发生错误", false);
};
} catch (error) {
console.error("连接失败:", error);
addMessage("连接服务器失败", false);
}
};
// 关闭连接
const closeConnection = () => {
if (webSocket.value && isConnected.value) {
webSocket.value.close(1000, "正常关闭");
}
};
// 发送消息
const sendMessage = () => {
if (!webSocket.value || !isConnected.value || !messageInput.value.trim())
return;
const message = messageInput.value.trim();
try {
webSocket.value.send(message);
addMessage(message, true);
messageInput.value = ""; // 清空输入框
} catch (error) {
console.error("发送消息失败:", error);
addMessage("发送消息失败", false);
}
};
// 添加消息到历史记录
const addMessage = (content: string, isSent: boolean) => {
messageHistory.value.push({
content,
isSent,
timestamp: formatTime(),
});
// 保持滚动到底部
setTimeout(() => {
const container = document.querySelector(".message-history");
if (container) {
container.scrollTop = container.scrollHeight;
}
}, 0);
};
// 组件挂载时连接
onMounted(() => {
connect();
});
// 组件卸载时关闭连接
onUnmounted(() => {
closeConnection();
});
// 监听窗口关闭事件
watch(
isConnected,
(newVal) => {
if (newVal) {
window.addEventListener("beforeunload", closeConnection);
} else {
window.removeEventListener("beforeunload", closeConnection);
}
},
{ immediate: true }
);
</script>
<style scoped>
.websocket-client {
max-width: 800px;
margin: 20px auto;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.connection-status {
margin: 15px 0;
padding: 10px;
border-radius: 4px;
background-color: #f5f5f5;
}
.connected {
color: #4caf50;
font-weight: bold;
}
.disconnected {
color: #f44336;
font-weight: bold;
}
.message-controls {
display: flex;
gap: 10px;
margin: 20px 0;
}
.message-controls input {
flex: 1;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.message-controls button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background-color: #2196f3;
color: white;
cursor: pointer;
transition: background-color 0.3s;
}
.message-controls button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.message-controls button:not(:disabled):hover {
background-color: #0b7dda;
}
.message-history {
margin-top: 20px;
padding: 15px;
border: 1px solid #e0e0e0;
border-radius: 4px;
max-height: 400px;
overflow-y: auto;
}
.message-item {
margin: 8px 0;
padding: 8px 12px;
border-radius: 4px;
max-width: 70%;
}
.sent {
background-color: #e3f2fd;
float: right;
}
.received {
background-color: #f1f8e9;
float: left;
}
.time {
display: block;
font-size: 12px;
color: #757575;
margin-top: 4px;
}
.message-item::after {
content: "";
clear: both;
display: table;
}
</style>
#八、测试,示例如下:
运行前端服务并访问测试页面(显示“已连接”即表示成功):
发送消息后,若后端控制台有显示,则表明发送成功: