实现简易聊天室功能
1.前端设计
<template><div><div><!-- 连接按钮 --><el-button v-loading="loading" :disabled="loading || isConnected" type="primary" @click="connect">{{ isConnected ? "已连接" : "连接" }}</el-button><!-- 断开连接按钮 --><el-button :disabled="!isConnected" type="danger" @click="disconnect">断开</el-button><!-- 发送消息按钮 --><el-button :disabled="!isConnected" type="success" @click="sendMessage">发送</el-button></div><div><el-input v-model="username" :disabled="isConnected" placeholder="用户名" style="width: 100px"@keyup.enter="sendMessage"/></div><div><!-- 消息输入框 --><el-input v-model="msg" placeholder="输入消息" @keyup.enter="sendMessage"/></div><!-- 消息列表 --><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: null, // 当前用户msg: '', // 发送的消息messages: [], // 消息列表isConnected: false, // 连接状态loading: false, // 是否正在连接};},methods: {// 连接 WebSocketconnect() {const vm = this// 参数校验if (vm.isConnected) {vm.$message.success("WebSocket 已连接");return;} else if (!vm.username) {vm.$message.success("请输入用户名");return;}// 连接vm.loading = true;vm.socket = new WebSocket(`/socket/ws/chat/${this.username}`);// 连接成功vm.socket.onopen = () => {vm.$notify.success("WebSocket 连接成功");vm.isConnected = true;vm.loading = false;};// 接收消息vm.socket.onmessage = (event) => {vm.messages.push(event.data);};// 关闭vm.socket.onclose = () => {vm.$notify.success("WebSocket 连接关闭");vm.cleanupSocket();};// 异常vm.socket.onerror = (error) => {vm.$notify.error("WebSocket 发生错误" + error);vm.cleanupSocket();};},// 发送消息sendMessage() {const vm = thisif (vm.socket && vm.isConnected) {vm.socket.send(vm.msg);vm.messages.push(`我: ${vm.msg}`);vm.msg = "";} else {vm.$notify.warning("WebSocket 未连接,无法发送消息");}},// 断开 WebSocketdisconnect() {const vm = thisif (vm.socket) {vm.socket.close();}vm.isConnected = false;},// 关闭 WebSocket 并清理cleanupSocket() {const vm = thisif (vm.socket) {vm.socket.close();vm.socket = null;}vm.isConnected = false;vm.loading = false;},},beforeUnmount() {this.cleanupSocket();},
};
</script><style scoped>
</style>
2.后端
依赖
<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>
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 {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}
控制类
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Controller;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@Controller
@ServerEndpoint("/ws/chat/{username}")
public class WebSocketServer {// 存储在线用户(线程安全)private static final Map<String, Session> userSessionMap = new ConcurrentHashMap<>();private Session session;private String username;/*** 创建时触发 用户在进入聊天室的时候触发** @param username* @param session*/@OnOpenpublic void onOpen(@PathParam("username") String username, Session session) {this.username = username;this.session = session;userSessionMap.put(username, session);System.out.println("【open】用户 " + username + " 连接成功,当前在线人数:" + userSessionMap.size());sendOne("【open-one】" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));sendAll("【open-all】用户 " + username + " 加入聊天室");}/*** 响应字符串** @param message*/@OnMessagepublic void onMessage(String message) {System.out.println("收到消息:" + message);System.out.println("当前用户" + username);sendAllNotMe(username + ":" + message);}/*** 响应字节流** @param session session* @param message message*/@OnMessagepublic void onMessage(Session session, byte[] message) {System.out.println("响应字节流");}/*** 断开 离开聊天室的时候触发*/@OnClosepublic void onClose(Session session, CloseReason closeReason) {userSessionMap.remove(username);System.out.println("用户 " + username + " 断开连接,当前在线人数:" + userSessionMap.size());System.out.println("连接关闭:" + session.getId());sendAll("【all】用户 " + username + " 离开聊天室");}@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("用户 " + username + " 发生错误:" + error.getMessage());try {this.session.close();userSessionMap.remove(username);} catch (IOException e) {e.printStackTrace();}}/*** 发送给所有人* @param message*/private void sendAll(String message) {if (userSessionMap != null && userSessionMap.size() > 0) {for (Map.Entry<String,Session> entry : userSessionMap.entrySet()) {try {entry.getValue().getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}}}/*** 发送给除了我的* @param message*/private void sendAllNotMe(String message) {if (userSessionMap != null && userSessionMap.size() > 0) {for (Map.Entry<String,Session> entry : userSessionMap.entrySet()) {try {if (StrUtil.isNotBlank(entry.getKey()) && entry.getKey().equals(username)) {continue;} else {entry.getValue().getBasicRemote().sendText(message);}} catch (IOException e) {e.printStackTrace();}}}}/*** 发送消息给某个用户*/private void sendOne(String message) {try {if (userSessionMap.containsKey(username)) {userSessionMap.get(username).getBasicRemote().sendText(message);}} catch (IOException e) {e.printStackTrace();}}
}