一、前言 本节简单介绍下如何在Spring Boot引入WebSocket,实现简单的客户端与服务端建立长连接并互发送文本消息。
二、框架搭建 2.1 依赖引入 1 2 3 4 5 6 7 8 9 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
2.2 构建服务端 新建handler包,然后在该包下新建MyStringWebSocketHandler继承TextWebSocketHandler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Component @Slf4j public class MyStringWebSocketHandler extends TextWebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) { log.info("和客户端建立连接"); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { session.close(CloseStatus.SERVER_ERROR); log.error("连接异常", exception); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); log.info("和客户端断开连接"); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 获取到客户端发送过来的消息 String receiveMessage = message.getPayload(); log.info(receiveMessage); // 发送消息给客户端 session.sendMessage(new TextMessage(fakeAi(receiveMessage))); // 关闭连接 // session.close(CloseStatus.NORMAL); } private static String fakeAi(String input) { if (input == null || "".equals(input)) { return "你说什么?没听清"; } return input.replace('你', '我') .replace("吗", "") .replace('?', '!') .replace('?', '!'); } }
该类重写了父类AbstractWebSocketHandler的四个方法:
1>afterConnectionEstablished,和客户端链接成功的时候触发该方法; 2>handleTransportError,和客户端连接失败的时候触发该方法; 3>afterConnectionClosed,和客户端断开连接的时候触发该方法; 4>handleTextMessage,和客户端建立连接后,处理客户端发送的请求。
此外,因为我们的目的是实现和客户端的通信,并且内容为文本内容,所以我们继承的是TextWebSocketHandler;如果传输的是二进制内容,则可以继承BinaryWebSocketHandler,更多信息可以自行查看WebSocketHandler的子类。
2.3 配置类 接着新建config包,然后在该包下新建WebSocketServerConfigure配置类:
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration @EnableWebSocket public class WebSocketServerConfigure implements WebSocketConfigurer { @Autowired private MyStringWebSocketHandler myStringWebSocketHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { webSocketHandlerRegistry.addHandler(myStringWebSocketHandler, "/connect").withSockJS(); } }
@EnableWebSocket用于开启WebSocket相关功能,我们注入了上面创建的MyStringWebSocketHandler,并将其注册到了WebSocketHandlerRegistry。
上面代码的含义是,当客户端通过/connecturl和服务端连接通信时,使用MyStringWebSocketHandler处理会话。withSockJS的含义是,通信的客户端是通过SockJS实现的,下面会介绍到。
三、构建客户端 SockJS是一个JS插件,用于构建WebSocket,兼容性好。
在resources目录下新建static包,然后在该包下新建client.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>WebSocket客户端</title> <script src="https://cdn.bootcss.com/sockjs-client/0.3.4/sockjs.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <style> .jumbotron { width: 100%; } #text { height: 3rem; font-size: 1rem; line-height: 3rem; margin: 1rem; } .btn { margin-right: 5px; } #connect { margin-left: 1rem; } #log { margin: 1rem 0 0 1rem; } </style> <div class="container"> <div class="row"> <div class="jumbotron"> <input type="text" placeholder="请输入你想传输的内容" id="text" class="col-lg-12"/> <input type="button" value="连接" class="btn btn-info" id="connect" onclick="connect()"/> <input type="button" value="发送" class="btn btn-success" id="sent" disabled="disabled" onclick="sent()"/> <input type="button" value="断开" class="btn btn-danger" id="disconnect" disabled="disabled" onclick="disconnect()"/> <div id="log"> <p>聊天记录:</p> </div> </div> </div> </div> <script type="text/javascript"> let text = document.querySelector('#text'); let connectBtn = document.querySelector("#connect"); let sentBtn = document.querySelector("#sent"); let disconnectBtn = document.querySelector("#disconnect"); let logDiv = document.querySelector("#log"); let ws = null; function connect() { let targetUri = "/connect"; ws = new SockJS(targetUri); ws.onopen = function () { setConnected(true); log('和服务端连接成功!'); }; ws.onmessage = function (event) { log('服务端说:' + event.data); }; ws.onclose = function () { setConnected(false); log('和服务端断开连接!') } } function sent() { if (ws != null) { ws.send(text.value); log('客户端说:' + text.value); } else { log('请先建立连接!') } } function disconnect() { if (ws != null) { ws.close(); ws = null; } setConnected(false); } function log(value) { let content = document.createElement('p'); content.innerHTML = value; logDiv.appendChild(content); text.value = ''; } function setConnected(connected) { connectBtn.disabled = connected; disconnectBtn.disabled = !connected; sentBtn.disabled = !connected; } </script> </body> </html>
html,css那些都不重要,重要的是我们引入了SockJS库。在connect()方法中,我们通过new SockJS(/connect)和上面的服务端建立了Socket通信。SockJS对象包含几个常用的实用方法:
1>onopen,和服务端讲了连接后的回调方法; 2>onmessage,服务端返回消息时的回调方法; 3>onclose,和服务端断开连接的回调方法; 4>send,发送消息给服务端; 5>close,断开和服务端的连接。
上面的JS较为简单,其他逻辑自己看看吧。
四、通信测试 启动项目,浏览器访问: http://localhost:8080/client.html :