导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>3.0.4</version> </dependency>
创建一个WebSocketConfig类
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
创建一个WebSocketServer类
package com.czf.study.wevSocket; import lombok.extern.slf4j.Slf4j; import org.junit.platform.commons.util.StringUtils; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint("/websocket/{userId}") @Component @Slf4j public class WebSocketServer { /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/ private static int onlineCount = 0; /**concurrent包的线程安全集合,也可以map改成set,用来存放每个客户端对应的MyWebSocket对象。*/ private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>(); /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/ private Session session; /**接收userId*/ private String userId=""; /** * 连接建立成功调用的方法*/ @OnOpen public void onOpen(Session session,@PathParam("userId") String userId) { this.session = session; this.userId=userId; if(!webSocketMap.containsKey(userId)){ //加入集合中 webSocketMap.put(userId,this); //在线数加1 addOnlineCount(); } log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); try { sendMessage("连接成功"); } catch (IOException e) { log.error("用户:"+userId+",网络异常!!!!!!"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); //从集合中删除 subOnlineCount(); } log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { log.info("【websocket消息】收到客户端发来的消息:{}", message); } /** * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("用户错误:"+this.userId+",原因:"+error.getMessage()); error.printStackTrace(); } /** * 实现服务器主动推送 */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 发送自定义消息 * */ public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException { log.info("发送消息到:"+userId+",报文:"+message); if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){ webSocketMap.get(userId).sendMessage(message); }else{ log.error("用户"+userId+",不在线!"); } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } }
服务端发请求接口
外面创建一个接口,模拟服务端发请求给客户端
@RestController public class DemoController { @RequestMapping("/push/{toUserId}") public ResponseEntity<String> pushToWeb(String message, @PathVariable String toUserId) throws IOException { WebSocketServer.sendInfo(message,toUserId); return ResponseEntity.ok("MSG SEND SUCCESS"); } }
测试
此时控制台输出
2022-11-15 11:51:43.009 INFO 28972 --- [nio-8787-exec-5] com.czf.study.wevSocket.WebSocketServer : 用户连接:1,当前在线人数为:1
接下来,模拟服务端给客户端发送请求,建立一个http请求
控制台输出
客户端收到请求
双人聊天室
webSocket经常被用作聊天室,两个客户端,通过一个服务端分发请求,进行沟通。
在此案例中,通过
/** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { log.info("【websocket消息】收到客户端发来的消息:{}", message); String[] split = message.split("/"); try { sendInfo(split[0],split[1]); } catch (IOException e) { e.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"> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <title>websocket测试页面</title> </head> <body> <div class="panel panel-default"> <div class="panel-body"> <div class="row"> <div class="col-md-6"> <div class="input-group"> <span class="input-group-addon">ws地址</span> <input type="text" id="address" class="form-control" placeholder="ws地址" aria-describedby="basic-addon1" value="ws://localhost:9700/tunnel/websocket"> <div class="input-group-btn"> <button class="btn btn-default" type="submit" id="connect">连接</button> </div> </div> </div> </div> <div class="row" style="margin-top: 10px;display: none;" id="msg-panel"> <div class="col-md-6"> <div class="input-group"> <span class="input-group-addon">消息</span> <input type="text" id="msg" class="form-control" placeholder="消息内容" aria-describedby="basic-addon1"> <div class="input-group-btn"> <button class="btn btn-default" type="submit" id="send">发送</button> </div> </div> </div> </div> <div class="row" style="margin-top: 10px; padding: 10px;"> <div class="panel panel-default"> <div class="panel-body" id="log" style="height: 450px;overflow-y: auto;"> </div> </div> </div> </div> </div> <script src="//i2.wp.com/cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script> <script src="//i2.wp.com/cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <script type="text/javascript"> $(function () { var _socket; $("#connect").click(function () { var tunnelId = "2"; // 设置需要传递的参数 _socket = new _websocket($("#address").val(), tunnelId); _socket.init(); }); $("#send").click(function () { var _msg = $("#msg").val(); output("发送消息:" + _msg); _socket.client.send(_msg); }); }); function output(e) { var _text = $("#log").html(); $("#log").html(_text + "<br>" + e); } function _websocket(address, tunnelId) { this.address = address; this.tunnelId = tunnelId; console.log(address) console.log(tunnelId) this.client; this.init = function () { if (!window.WebSocket) { this.websocket = null; return; } var _this = this; // var _client = new window.WebSocket(_this.address + "/" + _this.tunnelId);// 路径传参(没跑通) // 注意这里的名字要和后端接参数的名字对应上 var _client = new window.WebSocket(_this.address + "?tunnelId=" + tunnelId); _client.onopen = function () { output("websocket打开"); $("#msg-panel").show(); }; _client.onclose = function () { _this.client = null; output("websocket关闭"); $("#msg-panel").hide(); }; _client.onmessage = function (evt) { output(evt.data); }; _this.client = _client; }; return this; } </script> </body> </html>