基于websocket搭建聊天室
1.后端配置
1.依赖一个web一个websocket
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.config
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/ws/chat/{username}")
public class WebSocketServer {
private static final CopyOnWriteArraySet<WebSocketServer> clients = new CopyOnWriteArraySet<>();
// 存储在线用户(线程安全)
private static final Map<String, Session> userMap = new ConcurrentHashMap<>();
private Session session;
private String username; // 当前用户
@OnOpen
public void onOpen(@PathParam("username") String username, Session session) {
this.username = username;
userMap.put(username, session);
this.session = session;
clients.add(this);
System.out.println("用户 " + username + " 连接成功,当前在线人数:" + clients.size());
sendMessage("欢迎 " + username + " 加入聊天室!");
}
@OnMessage
public void onMessage(String message) {
System.out.println("收到消息:" + message);
broadcast(message);
}
@OnClose
public void onClose() {
clients.remove(this);
userMap.remove(username);
System.out.println("用户 " + username + " 断开连接,当前在线人数:" + clients.size());
broadcast("用户 " + username + " 离开聊天室");
System.out.println("连接关闭:" + session.getId());
}
@OnError
public void onError(Session session, Throwable error) {
System.out.println("用户 " + username + " 发生错误:" + error.getMessage());
}
private void broadcast(String message) {
for (WebSocketServer client : clients) {
try {
client.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送消息给某个用户
*/
private void sendMessage(String message) {
try {
if (userMap.containsKey(username)) {
userMap.get(username).getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.前端
1.vue2+element ui
2.前端代理vue.config.js
"use strict"
const path = require("path")
function resolve(dir) {
return path.join(__dirname, dir)
}
const name = "vue Template"
module.exports = {
publicPath: "./",
assetsDir: "static",
lintOnSave: false,
devServer: {
proxy: {
"^/socket": {
target: "ws://localhost:8888",
ws: true,
changeOrigin: true,
pathRewrite: {'/socket':''}
},
"^/api": {
target: "http://localhost:8888",
changeOrigin: true,
// pathRewrite: {'/datashare':'/'}
},
},
},
productionSourceMap: false,
configureWebpack: {
name: name,
resolve: {
alias: {
"@": resolve("src"),
},
},
},
}
3.测试代码
<template>
<div>
<!-- 连接按钮 -->
<el-button type="primary" :disabled="loading || isConnected" @click="connect" v-loading="loading">
{{ isConnected ? "已连接" : "连接" }}
</el-button>
<!-- 断开连接按钮 -->
<el-button type="danger" :disabled="!isConnected" @click="disconnect">
断开连接
</el-button>
<!-- 消息输入框 -->
<el-input v-model="msg" placeholder="输入消息" @keyup.enter="sendMessage"/>
<!-- 发送消息按钮 -->
<el-button type="success" :disabled="!isConnected" @click="sendMessage">
发送
</el-button>
<!-- 消息列表 -->
<el-card v-if="messages.length > 0">
<p v-for="(message, index) in messages" :key="index">{{ message }}</p>
</el-card>
</div>
</template>
<script>
export default {
name: "WebSocketChat",
data() {
return {
socket: null, // WebSocket 对象
username: "Jack", // 当前用户
msg: "", // 发送的消息
messages: [], // 消息列表
isConnected: false, // 连接状态
loading: false, // 是否正在连接
};
},
methods: {
// 连接 WebSocket
connect() {
if (this.isConnected) {
console.log("WebSocket 已连接");
return;
}
this.loading = true;
this.socket = new WebSocket(`/socket/ws/chat/${this.username}`);
this.socket.onopen = () => {
console.log("WebSocket 连接成功");
this.isConnected = true;
this.loading = false;
};
this.socket.onmessage = (event) => {
console.log("收到消息:" + event.data);
this.messages.push(event.data);
};
this.socket.onclose = () => {
console.warn("WebSocket 连接关闭");
this.isConnected = false;
this.cleanupSocket();
};
this.socket.onerror = (error) => {
console.error("WebSocket 发生错误", error);
this.isConnected = false;
this.cleanupSocket();
};
},
// 发送消息
sendMessage() {
if (this.socket && this.isConnected) {
this.socket.send(this.msg);
console.log("发送消息:" + this.msg);
this.messages.push(`我: ${this.msg}`);
this.msg = "";
} else {
console.warn("WebSocket 未连接,无法发送消息");
}
},
// 断开 WebSocket
disconnect() {
if (this.socket) {
this.socket.close();
}
this.isConnected = false;
},
// 关闭 WebSocket 并清理
cleanupSocket() {
if (this.socket) {
this.socket.close();
this.socket = null;
}
this.isConnected = false;
this.loading = false;
},
},
beforeUnmount() {
this.cleanupSocket();
},
};
</script>
<style scoped>
</style>