先看效果
一人分饰多角(bushi)
后端代码
先引入websocket依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
添加 WebSocketConfig 配置
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configurationpublic class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}
实体bean接收客户端发过来的信息
@Datapublic class SocketMsg {/** * 聊天类型 0 群聊 1 单聊 **/private int type;/** * 发送者 **/private String sendOutUser;/** * 接受者 **/private String receiveUser;/** * 消息 **/private String msg;}
WebSocketUtil
import cn.hutool.json.JSONUtil;import org.springframework.stereotype.Component;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArraySet;/** * WebSocket 连接测试 */@Component@ServerEndpoint("/web-socket/{userName}")public class WebSocketUtil {private String userName;private Session session;/** 固定前缀*/private static final String USER_NAME_PREFIX = "user_name_";/** * 用来存放每个客户端对应的MyWebSocket对象。 **/private static CopyOnWriteArraySet<WebSocketUtil> webSocketSet = new CopyOnWriteArraySet<>();/** * 存放Session集合,方便推送消息 (javax.websocket.Session) */private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();/** * 私聊:向指定客户端推送消息 */public synchronized static void privateMessage(SocketMsg socketMsg) {//接收消息的用户Session receiveUser = sessionMap.get(USER_NAME_PREFIX + socketMsg.getReceiveUser());//发送给接收者if(receiveUser != null){//发送给接收者System.out.println(socketMsg.getSendOutUser()+" 向 "+socketMsg.getReceiveUser()+" 发送了一条消息:"+socketMsg.getMsg());receiveUser.getAsyncRemote().sendText(socketMsg.getSendOutUser()+":"+socketMsg.getMsg());}else{//发送消息的用户System.out.println(socketMsg.getSendOutUser()+" 私聊的用户 "+socketMsg.getReceiveUser()+" 不在线或者输入的用户名不对");Session sendOutUser = sessionMap.get(USER_NAME_PREFIX + socketMsg.getSendOutUser());//将系统提示推送给发送者sendOutUser.getAsyncRemote().sendText("系统消息:对方不在线或者您输入的用户名不对");}}/** * 群聊:公开聊天记录 * @param userName 发送者的用户名称(当前用户) * @param message 发送的消息 * @param flag 用来标识 是否要将消息推送给 当前用户 */public synchronized static void publicMessage(String userName,String message,boolean flag) {for (WebSocketUtil item : webSocketSet) {Session session = item.session;if (flag){session.getAsyncRemote().sendText(message);}else {//获取发送这条消息的用户Session currentUser = sessionMap.get(USER_NAME_PREFIX + userName);//消息不用推送到发送者的客户端if (!session.getId().equals(currentUser.getId())){session.getAsyncRemote().sendText(message);}}}System.out.println("公共频道接收了一条消息:"+message);}/** * 监听:连接成功 * @param session * @param userName 连接的用户名 */@OnOpenpublic void onOpen(Session session, @PathParam("userName") String userName) {this.userName = userName;this.session = session;sessionMap.put(USER_NAME_PREFIX + userName, session);webSocketSet.add(this);//在线数加1String tips = userName+" 加入聊天室。当前聊天室人数为" + webSocketSet.size();System.out.println(tips);publicMessage(userName,tips,true);}/** * 监听:收到客户端发送的消息 * @param message 发送的信息(json格式,里面是 SocketMsg 的信息) */@OnMessagepublic void onMessage(String message) {if (JSONUtil.isTypeJSONObject(message)) {SocketMsg socketMsg = JSONUtil.toBean(message, SocketMsg.class);if(socketMsg.getType() == 1){//单聊,需要找到发送者和接受者privateMessage(socketMsg);}else{//群发消息publicMessage(socketMsg.getSendOutUser(),socketMsg.getSendOutUser()+": "+socketMsg.getMsg(),false);}}}/** * 监听: 连接关闭 */@OnClosepublic void onClose() {if (sessionMap.containsKey(USER_NAME_PREFIX + userName)) {//连接关闭后,将此websocket从set中删除sessionMap.remove(USER_NAME_PREFIX + userName);webSocketSet.remove(this);}String tips = userName+" 退出聊天室。当前聊天室人数为" + webSocketSet.size();System.out.println(tips);publicMessage(userName,tips,true);}/** * 监听:发生异常 * @param error */@OnErrorpublic void onError(Throwable error) {System.out.println("userName为:" + userName + ",发生错误:" + error.getMessage());error.printStackTrace();}}
前端代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>聊天室</title><script src="https://code.jquery.com/jquery-3.3.1.min.js"></script><style type="text/css">input{width: 150px;height: 30px;line-height: 25px;padding: 5px 10px;border-radius: 5px;border: 2px solid;font-size: 16px;}#msg{width: 300px;}button{width: 80px;height: 44px;padding: 5px 20px;border-radius: 5px;}</style></head><body>聊天室<br/><br/><input type="text" id="sendOutUser" placeholder="自己的用户名"><button onclick="connectWebSocket()">上线</button><button onclick="closeWebSocket()">下线</button><br/><br><input type="text" id="msg" placeholder="要发送的信息"/><input type="text" id="receiveUser" placeholder="接收人的用户名"/><button onclick="send()">发送</button><br><br><hr><div id="msgList"></div><script type="text/javascript">var websocket = null;//连接WebSocketfunction connectWebSocket() {var sendOutUser = document.getElementById("sendOutUser").value;if (sendOutUser === "") {alert("请输入用户名");return;}//判断当前浏览器是否支持websocketif ('WebSocket' in window) {websocket = new WebSocket("ws://localhost:7070/web-socket/"+document.getElementById("sendOutUser").value);} else {alert('当前浏览器 not support websocket')}//连接发生错误的回调方法websocket.onerror = function () {alert("连接发生错误");};//连接成功建立的回调方法websocket.onopen = function () {var sendOutUser = document.getElementById("sendOutUser")sendOutUser.readOnly = truesendOutUser.style.backgroundColor='#ddd'}//接收到消息的回调方法websocket.onmessage = function (event) {console.log(event.data)innerdiv("",event.data)}//连接关闭的回调方法websocket.onclose = function () {innerdiv("","websocket连接关闭");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function () {closewebsocket();}}//关闭连接function closeWebSocket() {websocket.close();}//发送消息function send() {var m = new Map(); // 空Mapvar sendOutId = document.getElementById("sendOutUser")//发送者var msg = document.getElementById("msg").value//发送消息if (msg === "") {alert("请输入消息");return;}var receiveUser = document.getElementById("receiveUser").value //接收者m.set("sendOutUser",sendOutUser.value);m.set("msg",msg)// 接收者为空时,type为群聊,否则为私聊if (receiveUser === "") {m.set("type",0)}else{m.set("receiveUser",receiveUser)m.set("type",1)}json = mapToJson(m)websocket.send(json)innerdiv("我",msg)}//map转换为jsonfunctionmapToJson(map) {var obj= Object.create(null);for (var[k,v] of map) {obj[k] = v;}return JSON.stringify(obj);}//显示聊天记录到页面function innerdiv(id,txt){var msgList = document.getElementById("msgList")if (id === "") {msgList.innerHTML += "" + txt + "
"}else{msgList.innerHTML += ""+ id +": "+txt+ "
"}}</script></body></html>