websocket无非就是能够让我们的浏览器与浏览器之间、浏览器与服务器做到及时交互,
目前来说,我使用到的场景就两个:1.用户与用户的聊天室,2.用户与AI的聊天室
websocket实际用起来比较简单,
从后端来说,以springboot为例,
导入依赖坐标
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
载入配置开启websocket服务
没有正确实现的话,控制台会报错No mapping for get /** 。原因就是spring没有正确识别websocket的路径映射,websocket的路径映射不仅需要@ServerEndpoint注解,还需要spring正确扫描并注册websocket端点。因此我们不仅需要导入websocket的config,还需要确保spring正确管理serverEndpoint
@Configuration @Slf4j @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {log.info("开启websocket配置...");// 自定义的 WebSocketHandler,可以在这里注册// registry.addHandler(myWebSocketHandler, "/websocketClient/{userId}"); } }
类的基础形式
@ServerEndpoint(value = "/websocketClient/{userId}") @Component @Slf4j public class WebSocketClient{}
常见成员变量
/*** 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的*/private static int onlineCount = 0;/*** concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象*/private static final ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();/*** websocket的会话对象,与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 用户id 唯一标识*/private String userId;
常见方法:onOpen(建立成功会调用的方法)、onClose(连接关闭调用的方法)、onMessage(收到客户端消息后调用的方法)、onError(发生错误时调用的方法)、sendMessage(向客户端推送消息)
onOpen方法
@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {this.session = session;this.userId = userId;//加入mapwebSocketMap.put(userId, this);log.info("WebSocket客户端{}连接成功,客户端标识:{},当前在线人数:{}", session.getId(), userId, getOnlineCount());}
onClose方法
@OnClosepublic void onClose() {//从map中删除 webSocketMap.remove(userId);log.info("WebSocket客户端{}连接断开,客户端标识:{},当前在线人数:{}", session.getId(), userId, getOnlineCount());}
onMessage方法
@OnMessagepublic void onMessage(String message, Session session) throws Exception {// 心跳检测响应if (StringUtils.equalsIgnoreCase("ping", message)) {sendMessage("pong");log.info("WebSocket服务端已回复客户端{}的心跳检测:pong", session.getId());return;}//todo 存入mysql之类}
onError方法
@OnErrorpublic void onError(Session session, Throwable error) {log.error("发生错误{}", session.getId(), error);error.printStackTrace();}
sendMessage方法
public void sendMessage(String message) {try {this.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();log.error("客户端{}发送消息{{}}失败", session.getId(), message);}}
在sendMessage方法的基础上实现了具体消息的发送:
如果是对具体的用户发送消息,则在websocketMap中取出对应的WebsocketClient再调用sendMessage方法
如果是群发消息,则直接遍历websocketMap并依次发送sendMessage即可
public static void sendMessageByUserId(String userId, String message) throws IOException {log.info("给用户{}发送{}信息", userId, message);if (StrUtil.isNotBlank(userId) && webSocketMap.containsKey(userId)) {webSocketMap.get(userId).sendMessage(message);}}
public static void sendInfo(String message) {for (String item : webSocketMap.keySet()) {webSocketMap.get(item).sendMessage(message);}}