WebSocket系列6---对用户进行多房间管理的解决方案
房间管理方式
使用websocket实现实时通讯的功能,有两种方式
- 只创建一个聊天的房间,所有人都往里面发送数据,然后接收数据的那一方,通过自己定义的规则,对接收到的数据进行筛选,获取自自己想要的数据。
- 创建多个两天的房间,每个房间管理自己的聊天信息,接收方不用对数据进行筛选,因为只能接收自己所在房间的信息,所以无需过滤。
单房间管理
package com.green.rainbow.modules.app.socket;import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketService {// concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketService对象。private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet();// 与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session) {this.session = session;webSocketSet.add(this);try {sendMessage("连接成功");} catch (IOException e) {System.out.println("IO异常");}}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(String message, Session session) {for (WebSocketService item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();}}}/*** 发生错误时调用* */@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {webSocketSet.remove(this);}public void sendMessage(String message) throws IOException {// websocket session发送文本消息有两个方法:// getAsyncRemote()和getBasicRemote() 推荐使用getAsyncRemote()这个方法// getBasicRemote是阻塞式的,getAsyncRemote是非阻塞式的// 。。。别问我,你在这里为什么使用getBasicRemotethis.session.getBasicRemote().sendText(message);}
}
多房间管理
package com.example.demo.socket;import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@ServerEndpoint(value = "/websocket/{info}")
@Component
public class WebSocketService {private static SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//创建时间格式对象//创建房间的集合,使用ConcurrentHashMap是为了保证线程安全,HashMap在多线程的情况下会出现问题private static ConcurrentHashMap> roomList = new ConcurrentHashMap<>();// 与某个客户端的连接会话,需要通过他来给客户端发送消息private Session session;//重新加入房间标识private int rejoin = 0;//info 格式: 标识|房间名|用户名(保证唯一性)@OnOpenpublic void onOpen(@PathParam("info")String param, Session session){// 建立连接的时候,通过获取地址上的{info}的信息,通过截取字符串获取连接人的登录信息this.session = session;// flag 可以定义两个类型 joinRoom(加入房间) , existRoom(退出房间) 和 chatRoom (聊天)String flag = param.split("[|]")[0];// roomId 房间号String roomId = param.split("[|]")[1];if(flag.equals("joinRoom")){// 用户名(必须保证唯一性)String nickname = param.split("[|]")[2];this.joinRoom(roomId , nickname);}}//加入房间public void joinRoom(String roomId, String nickname){if (!roomList.containsKey(roomId)) {// 创建房间不存在时,创建房间ConcurrentHashMap room = new ConcurrentHashMap<>();// 添加用户room.put(nickname, this);roomList.put(roomId, room);} else {// 房间已存在,直接添加用户到相应的房间ConcurrentHashMap room = roomList.get(roomId);if (room.get(nickname) != null) {this.rejoin = 1;}else{room.put(nickname, this);}//发送消息给房间内的其他人,通知他们user已经上线for(String item : room.keySet()){Map map = new HashMap<>();map.put("flag", "joinRoom");map.put("name", item);map.put("status", "上线");room.get(item).sendMessage(map);}}}//发送消息public void sendMessage(Map map){JSONObject jsonObject = JSONObject.fromObject(map);try {// websocket session发送文本消息有两个方法:// getAsyncRemote()和getBasicRemote() 推荐使用getAsyncRemote()这个方法// getBasicRemote是阻塞式的,getAsyncRemote是非阻塞式的// 。。。别问我,你在这里为什么使用getBasicRemotethis.session.getBasicRemote().sendText(jsonObject.toString());} catch (IOException e) {e.printStackTrace();}}//接收来自用户的消息@OnMessagepublic void onMessage(String message, Session session) throws IOException{//把用户发来的消息解析成json对象JSONObject param = JSONObject.fromObject(message);//如果是退出房间 flag 分类:exitRoom 离开 joinRoom 加入房间 chatRoom 聊天String flag = (String)param.get("flag");if(flag.equals("exitRoom")){String roomId = (String )param.get("roomId");String nickname = (String)param.get("nickname");ConcurrentHashMap room = roomList.get(roomId);//获取房间room.remove(nickname);//从房间中移除该用户//判断房间中是否有人,如果没人则删除房间,否则通知其他用户该用户已经下线if(room.size() == 0 ){roomList.remove(roomId);}else{for(String item : room.keySet()){param.put("status", "下线");room.get(item).sendMessage(param);}}}else if(flag.equals("chatRoom")){param.put("date", df.format(new Date()));String roomId = (String)param.get("roomId");String nickname = (String)param.get("nickname");ConcurrentHashMap room = roomList.get(roomId);if(room.get(nickname).rejoin == 0 ){for(String item: room.keySet()){room.get(item).sendMessage(param);}}else{room.get(nickname).sendMessage(param);}}}//用户断开@OnClosepublic void onClose(Session session){System.out.print("onClose");}@OnErrorpublic void onError(Throwable t){System.out.print("onError");}}
Http拦截器也会拦截ws/wss的path
你的项目的拦截器或者过滤器,拦截的是所有路径(/*)那么他也会拦截ws://localhost:8080/websocket 的“/websocket ” 路径,所以会连接不上。解决的方法就是,在你的项目中配置一下不拦截这个路径。
Reference: https://blog.csdn.net/qq_39774931/article/details/83897837
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
