实现简易聊天室功能
目录
实现简易聊天室功能
实现简易聊天室功能
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: {
// 连接 WebSocket
connect() {
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 = this
if (vm.socket && vm.isConnected) {
vm.socket.send(vm.msg);
vm.messages.push(`我: ${vm.msg}`);
vm.msg = "";
} else {
vm.$notify.warning("WebSocket 未连接,无法发送消息");
}
},
// 断开 WebSocket
disconnect() {
const vm = this
if (vm.socket) {
vm.socket.close();
}
vm.isConnected = false;
},
// 关闭 WebSocket 并清理
cleanupSocket() {
const vm = this
if (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 {
@Bean
public 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
*/
@OnOpen
public 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
*/
@OnMessage
public void onMessage(String message) {
System.out.println("收到消息:" + message);
System.out.println("当前用户" + username);
sendAllNotMe(username + ":" + message);
}
/**
* 响应字节流
*
* @param session session
* @param message message
*/
@OnMessage
public void onMessage(Session session, byte[] message) {
System.out.println("响应字节流");
}
/**
* 断开 离开聊天室的时候触发
*/
@OnClose
public void onClose(Session session, CloseReason closeReason) {
userSessionMap.remove(username);
System.out.println("用户 " + username + " 断开连接,当前在线人数:" + userSessionMap.size());
System.out.println("连接关闭:" + session.getId());
sendAll("【all】用户 " + username + " 离开聊天室");
}
@OnError
public 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();
}
}
}