本节简单介绍下如何在Spring Boot引入WebSocket,实现简单的客户端与服务端建立长连接并互发送文本消息。
框架搭建
新建一个Spring Boot项目,artifactId为spring-boot-websocket-socketjs,项目结构如下图所示:

项目的pom内容如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>cc.mrbird</groupId><artifactId>spring-boot-websocket-socketjs</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-boot-websocket-socketjs</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><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></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
引入了spring-boot-starter-websocket和spring-boot-starter-web依赖。
构建服务端
在cc.mrbird.socket目录下新建handler包,然后在该包下新建MyStringWebSocketHandler继承TextWebSocketHandler:
@Componentpublic class MyStringWebSocketHandler extends TextWebSocketHandler {private Logger log = LoggerFactory.getLogger(this.getClass());@Overridepublic void afterConnectionEstablished(WebSocketSession session) {log.info("和客户端建立连接");}@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {session.close(CloseStatus.SERVER_ERROR);log.error("连接异常", exception);}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {super.afterConnectionClosed(session, status);log.info("和客户端断开连接");}@Overrideprotected 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的四个方法:

- afterConnectionEstablished,和客户端链接成功的时候触发该方法;
- handleTransportError,和客户端连接失败的时候触发该方法;
- afterConnectionClosed,和客户端断开连接的时候触发该方法;
- handleTextMessage,和客户端建立连接后,处理客户端发送的请求。
WebSocketSession对象代表每个客户端会话,包含许多实用方法:

方法见名知意,就不赘述了。
此外,因为我们的目的是实现和客户端的通信,并且内容为文本内容,所以我们继承的是TextWebSocketHandler;如果传输的是二进制内容,则可以继承BinaryWebSocketHandler,更多信息可以自行查看WebSocketHandler的子类。
接着在cc.mrbird.socket目录下新建configure包,然后在该包下新建WebSocketServerConfigure配置类:
@Configuration@EnableWebSocketpublic class WebSocketServerConfigure implements WebSocketConfigurer {@Autowiredprivate MyStringWebSocketHandler myStringWebSocketHandler;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(myStringWebSocketHandler, "/connect").withSockJS();}}
@EnableWebSocket用于开启WebSocket相关功能,我们注入了上面创建的MyStringWebSocketHandler,并将其注册到了WebSocketHandlerRegistry。
上面代码的含义是,当客户端通过/connecturl和服务端连接通信时,使用MyStringWebSocketHandler处理会话。withSockJS的含义是,通信的客户端是通过SockJS实现的,下面会介绍到。
构建客户端
SockJS是一个JS插件,用于构建WebSocket,兼容性好。
在resources目录下新建static包,然后在该包下新建client.html:
<!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对象包含几个常用的实用方法:
onopen,和服务端讲了连接后的回调方法;onmessage,服务端返回消息时的回调方法;onclose,和服务端断开连接的回调方法;send,发送消息给服务端;close,断开和服务端的连接。
上面的JS较为简单,其他逻辑自己看看吧。
通信测试
启动项目,浏览器访问:http://localhost:8080/client.html:

源码连接:https://github.com/wuyouzhuguli/SpringAll/tree/master/76.spring-boot-websocket-socketjs。
