使用Java WebSocket能否向指定session发送消息及实现方案
实现方案
这个需求完全可以实现,基于你现有代码仅需做少量修改即可,步骤如下:
1. 调整存储结构
首先新增一个用户名到对应Endpoint实例的映射,方便直接通过目标用户名定位到需要发送的会话,同时替换原有HashMap为线程安全的ConcurrentHashMap避免并发问题:
// 替换原来的users HashMap private static final Map<String, String> sessionIdToUsername = new ConcurrentHashMap<>(); // 新增用户名到Endpoint的映射 private static final Map<String, ChatEndpoint> usernameToEndpoint = new ConcurrentHashMap<>();
2. 调整@OnOpen逻辑
新增的映射在连接建立时写入数据:
@OnOpen public void onOpen(Session session, @PathParam("username") String username) throws IOException, EncodeException { this.session = session; chatEndpoints.add(this); // 两个映射都赋值 sessionIdToUsername.put(session.getId(), username); usernameToEndpoint.put(username, this); Message message = new Message(); message.setFrom(username); message.setContent("Connected!"); broadcast(message); }
3. 新增单播发送方法
单独封装向指定用户发送消息的逻辑,需要你保证Message类已经新增to字段及对应的get/set方法:
private static void sendToUser(Message message) throws IOException, EncodeException { String targetUsername = message.getTo(); ChatEndpoint targetEndpoint = usernameToEndpoint.get(targetUsername); if (targetEndpoint != null) { synchronized (targetEndpoint) { targetEndpoint.session.getBasicRemote().sendObject(message); } } // 可自行补充目标用户不存在时的异常处理逻辑,比如给发送方返回提示 }
4. 调整@OnMessage逻辑
根据消息是否指定接收人判断走广播还是单播:
@OnMessage public void onMessage(Session session, Message message) throws IOException, EncodeException { String fromUsername = sessionIdToUsername.get(session.getId()); message.setFrom(fromUsername); // 判断是否有指定接收人 if (message.getTo() != null && !message.getTo().trim().isEmpty()) { sendToUser(message); } else { broadcast(message); } System.out.println(message.getContent()); }
可选:按sessionId发送的实现
如果你不需要按用户名发送,直接按sessionId发送,可以不用新增usernameToEndpoint映射,直接在单播方法里遍历匹配sessionId即可:
private static void sendToSession(Message message) throws IOException, EncodeException { String targetSessionId = message.getTo(); chatEndpoints.forEach(endpoint -> { if (endpoint.session.getId().equals(targetSessionId)) { synchronized (endpoint) { try { endpoint.session.getBasicRemote().sendObject(message); } catch (IOException | EncodeException e) { e.printStackTrace(); } } } }); }
补充连接关闭逻辑
记得新增@OnClose方法,连接关闭时清理对应映射数据避免内存泄漏:
@OnClose public void onClose(Session session) { chatEndpoints.remove(this); String username = sessionIdToUsername.remove(session.getId()); if (username != null) { usernameToEndpoint.remove(username); } }
内容的提问来源于stack exchange,提问作者coco
相关产品推荐
相关产品推荐

