# 手动部署 webSocket 相关 SpringMVC 服务 以及前端 webSocket 构建 *web 技术栏* 通过本文,您将学习到如何手动创建和部署 WebSocket 相关的 SpringMVC服务。这些技能将帮助您在实际开发中更好地应用 ws 协议相关知识,提高 web 应用程序(网站 等)的实时性和交互性。 ## 目录 [TOC]  ## 什么是 WebSocket WebSocket是一种网络通信协议,它提供了与TCP套接字类似的接口,使得客户端和服务器之间的全双工通信成为可能。WebSocket可以在HTTP协议的基础上升级使用,现称之为 WS 协议,使得它成为了一种高效、快速、实时的网络通信方式。在WebSocket的通信过程中,客户端和服务器需要建立连接,然后进行数据的传输和接收。与HTTP协议不同,WebSocket连接是持久化的,可以保持长时间的数据传输。因此,WebSocket被广泛应用于实时性要求较高的应用场景,如在线游戏、实时聊天、股票交易等。 ### 什么是 ws 协议 我们知道在 web 中,如果想要使用软件,就要通过一个网站进行访问,在这个访问过程中通常会需要一个http网址,形如`http://xxx.xxx` 又或者是 `http://xxx.xxx`,当进行访问的时候,系统会自动的根据网址找到对应的web服务器,使用[**http协议**](https://baike.baidu.com/item/HTTP/243074?fr=ge_ala "**http协议**")获取到网站数据,这样的访问方式是短连接,每个请求返回一次回复,不能满足私聊这类功能,因此对http进行了一个升级,称之为[ **ws 协议**](https://baike.baidu.com/item/WebSocket/1953845?fr=ge_ala " **ws 协议**"),其url格式形如`ws://xxx.xxx` 接下来我们将会频繁的解除它。 ## 后端 webSocket 构建 webSocket 的服务依赖一台服务器来进行,因此我们需要先准备一台具有webSocket服务的服务器,在这里我们将通过 SpringMVC 来实现这样的操作,因此我们需要先进行后端的构建,接下来,我们正式开始! ### 引入 maven 依赖 首先我们的 由于使用的是 SpringMVC,SpringMVC 已经实现了对于 webSocket 的支持,因此在这里需要导入 SpringMVC 的依赖。 ```xml <dependencies> <!-- 导入 Java Servlet 的依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!-- 导入 Spring MVC 的依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies> ``` ### 实现 SpringMVC 的 TextWebSocketHandler 处理类 TextWebSocketHandler是AbstractWebSocketHandler的子类,它会拒绝处理二进制消息。如果收到二进制消息的时候,将会关闭WebSocket连接。与之类似,BinaryWebSocketHandler也是AbstractWebSocketHandler的子类,它重载了handleTextMessage()方法,如果接收到文本消息的话,将会关闭连接。 **PS:如果有二进制数据只需要更改一个处理类的名字,其它的操作差不多,在这里使用文本处理类进行演示,便于进行数据观察 ** #### 查看 TextWebSocketHandler 中的可重写方法 下面就是一个文本处理类的基本框架,每个函数具有不同的意义,在这里进行了注释 ```java package top.lingyuzhao.SpringTest.controller; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.TextWebSocketHandler; /** * ws 后端处理类 * @author zhao */ public final class WsTest extends TextWebSocketHandler { // 连接操作建立之后 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 在连接建立后,可以在此处进行一些初始化操作 } // 接收到信息 @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { super.handleMessage(session, message); // 在接收到消息时调用,可以处理接收到的消息,根据消息的类型(文本、二进制等)进行相应的处理。 } // 接收到文本信息 @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); // 在接收到文本消息时调用,可以在此处处理接收到的文本消息。 } @Override protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { super.handlePongMessage(session, message); // 在接收到WebSocket的Pong消息时调用。Pong消息是WebSocket协议中的一种消息类型,它的作用主要是告诉服务器,客户端依然在线,并且可以接收服务器发送的消息。在某些情况下,如果服务器发送了消息给客户端,但是客户端没有及时响应,服务器就会发送一个Pong消息给客户端,以保持连接的活跃状态。在此方法中,可以处理接收到的Pong消息。 } // 发生了错误信息 @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { super.handleTransportError(session, exception); // 当发生传输错误时调用,可以在此处处理传输错误的情况。 } // 连接关闭之后 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); // 在WebSocket连接关闭之后调用,可以在此处进行一些关闭连接后的清理操作。 } @Override public boolean supportsPartialMessages() { return super.supportsPartialMessages(); // 返回是否支持部分消息。如果返回true,则表示支持部分消息;如果返回false,则表示不支持部分消息。 } } ``` #### 开始重写方法 在这里我们并不需要将所有的方法都实现,仅仅使用 webSocket 进行一个简单的文本传输演示,更能达到很好的学习效果,降低学习门槛。 ##### 重写 初始化函数 afterConnectionEstablished 在这里我们将在连接建立成功之后打印一些信息 ``` // 连接操作建立之后 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 在连接建立后,可以在此处进行一些初始化操作 System.out.println("与 " + session.getId() + " 的连接已建立!"); } ``` ##### 重写 文本消息处理函数 handleTextMessage ``` // 接收到文本信息 @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); // 在接收到文本消息时调用,可以在此处处理接收到的文本消息。 // TODO 在这里我们直接将数据稍加修改,然后回复给发送者 相当于是下面的样子 /* 前端 -> 你好 -> 后端 后端 -> 后端服务器接收到了:你好 -> 前端 */ session.sendMessage(new TextMessage( "后端服务器接收到了:" + message.getPayload() )); } ``` ##### 重写 连接关闭函数(相当于是连接的析构函数)afterConnectionClosed ``` // 连接即将关闭之后 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); // 在WebSocket连接关闭之后调用,可以在此处进行一些关闭连接后的清理操作。 // TODO 在这里我们打印一些数据 System.out.println("与 " + session.getId() + " 的连接已关闭!"); } ``` ##### 全类展示 ``` package top.lingyuzhao.SpringTest.controller; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.TextWebSocketHandler; /** * ws 后端处理类 * @author zhao */ public final class WsTest extends TextWebSocketHandler { // 连接操作建立之后 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 在连接建立后,可以在此处进行一些初始化操作 System.out.println("与 " + session.getId() + " 的连接已建立!"); } // 接收到信息 @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { super.handleMessage(session, message); // 在接收到消息时调用,可以处理接收到的消息,根据消息的类型(文本、二进制等)进行相应的处理。 } // 接收到文本信息 @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); // 在接收到文本消息时调用,可以在此处处理接收到的文本消息。 // TODO 在这里我们直接将数据稍加修改,然后回复给发送者 相当于是下面的样子 /* 前端 -> 你好 -> 后端 后端 -> 后端服务器接收到了:你好 -> 前端 */ session.sendMessage(new TextMessage( "后端服务器接收到了:" + message.getPayload() )); } @Override protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { super.handlePongMessage(session, message); // 在接收到WebSocket的Pong消息时调用。Pong消息是WebSocket协议中的一种消息类型,它的作用主要是告诉服务器,客户端依然在线,并且可以接收服务器发送的消息。在某些情况下,如果服务器发送了消息给客户端,但是客户端没有及时响应,服务器就会发送一个Pong消息给客户端,以保持连接的活跃状态。在此方法中,可以处理接收到的Pong消息。 } // 发生了错误信息 @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { super.handleTransportError(session, exception); // 当发生传输错误时调用,可以在此处处理传输错误的情况。 } // 连接即将关闭之后 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); // 在WebSocket连接关闭之后调用,可以在此处进行一些关闭连接后的清理操作。 // TODO 在这里我们打印一些数据 System.out.println("与 " + session.getId() + " 的连接已关闭!"); } @Override public boolean supportsPartialMessages() { return super.supportsPartialMessages(); // 返回是否支持部分消息。如果返回true,则表示支持部分消息;如果返回false,则表示不支持部分消息。 } } ``` ### 将处理类注册给 SpringMVC 首先我们需要创建一个 SpringMVC 的 WebSocket 配置类,其类型是 `WebSocketConfigurer` 我们需要拓展重写它的注册函数,并在注册函数中手动注册 我们写好的 webSocket 处理类。 相当于是我们手动干预了SpringMVC 的注册流程,在注册的流程中,我们注册了一个自己的处理类,下面就是代码以及对应的解释。 ``` package top.lingyuzhao.SpringTest.conf; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import top.lingyuzhao.SpringTest.controller.WsTest; /** * Spring mvc 的配置类 * 这是一个Spring MVC的配置类,用于设置和配置Spring MVC和WebSocket的相关设置。 * * @author zhao * 作者:赵 */ @Configuration // 标注这个类是一个配置类 @EnableWebMvc // 启用Spring MVC的功能 @EnableWebSocket // 启用WebSocket的功能 public class SpringConfig implements WebSocketConfigurer { // 这个类实现了一个WebSocket的配置接口 /** * 注册WebSocket处理器 * 这个方法用于注册WebSocket处理器,并设置相应的处理路径和拦截器。 */ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { webSocketHandlerRegistry.addHandler(new WsTest(), "/wsTest") // 添加一个WebSocket处理器,路径为/wsTest .addInterceptors(new HttpSessionHandshakeInterceptor()) // 在WebSocket握手时添加一个拦截器,用于处理HTTP会话 .setAllowedOrigins("*"); // 设置允许跨域访问的来源,星号代表所有来源都允许。 // 允许跨域访问,设置允许的来源为所有来源。 } } ``` ### 打包并部署到服务器(不同服务器软件部署方式不同,在这里暂时略过) ## 前端 webSocket 构建 ### webSocket 基础操作 #### 实例化 webSocket ```js // 指定后端的ws 服务路径 就是在进行注册时候添加的 /wsTest const p = "/wsTest"; // 指定后端程序的路径 根据不同服务器来进行设置 我这里打包的结果是 WebTest_war.war 路径是 WebTest_war const p1 = "WebTest_war"; // 在这里实例化出 webSocket 需要注意 在实例化成功的一瞬间,就会开始向后端发送连接数据 const webSocket = new WebSocket(`ws://localhost:8080/${p1}/${p}`); ``` #### 设置 webSocket 的各种事件 这个操作就很像是在前面的后端中实现 ws 处理类一样,有很多的函数会在不同的时机调用,而我们在前端设置事件回调函数的操作就是 JS 中的回调一样,不过事件的名字有一些区别,下面就是一些 ws JS 中事件的汇总。 ``` 连接建立(opening):在 WebSocket 连接建立之前,浏览器会使用 HTTP 请求来询问服务器是否支持 WebSocket,如果支持,WebSocket 协议就会用于两者之间的通信。 连接关闭(closing):WebSocket 连接关闭时,会触发 closing 事件。 连接已建立(open):WebSocket 连接建立后,会触发 open 事件。 收到消息(message):当 WebSocket 连接接收到消息时,会触发 message 事件。 错误(error):当 WebSocket 连接出现错误时,会触发 error 事件。 ``` 下面就是设置的代码 ```js // 设置连接成功之后的回调函数 webSocket.onopen = () => { console.info("连接建立成功!!!") } // 设置收到消息之后的回调函数 webSocket.onmessage = (message) => { console.info(message.data) } // 设置连接被断开之后的回调函数 webSocket.onclose = () => { console.info("连接被断开!!!") } ``` #### 在 JS 中使用 webSocket 发送数据 下面是一个 webSocket 发送数据的调用示例 ``` webSocket.send("你好") ``` 当连接建立之后 通过调用 send 函数就可以实现数据发送,因此我们可以直接将发送动作绑定在连接完毕的回调中,下面就是代码 ```js // 设置连接成功之后的回调函数 webSocket.onopen = () => { console.info("连接建立成功!!!") webSocket.send("你好") console.info("已发送数据 ”你好“") } ``` #### 运行 HTML 文件 当我们运行文件之后 F12 终端 打印的执行结果如下所示。 ``` 连接建立成功!!! 已发送数据 ”你好“ 后端服务器接收到了:你好 ``` ------ ***操作记录*** 作者:[root](http://www.lingyuzhao.top/index.html?search=1 "root") 操作时间:2023-12-09 21:40:58 星期六 事件描述备注:保存/发布 [](如果不需要此记录可以手动删除,每次保存都会自动的追加记录)