SpringBoot之WebSocket
1.前言
最近在弄这个 大疆机场 的东西,需要用到了 websocket 进行通讯,于是就在源码的基础上研究了下这个 websocket 的东西。
WebSocket 是一种基于 TCP 协议的全双工通信协议,它允许客户端和服务器之间建立持久的、双向的通信连接。相比传统的 HTTP 请求 - 响应模式,WebSocket 提供了实时、低延迟的数据传输能力。通过 WebSocket,客户端和服务器可以在任意时间点互相发送消息,实现实时更新和即时通信的功能。WebSocket 协议经过了多个浏览器和服务器的支持,成为了现代 Web 应用中常用的通信协议之一。它广泛应用于聊天应用、实时数据更新、多人游戏等场景,为 Web 应用提供了更好的用户体验和更高效的数据传输方式。
2.集成
集成的方式有很两种:
(1)是使用由 Jakarta EE 规范提供的 Api,也就是 jakarta.websocket 包下的接口。
(2)是使用 spring 提供的支持,也就是 spring-websocket 模块。前者是一种独立于框架的技术规范,而后者是 Spring 生态系统的一部分,可以与其他 Spring 模块(如 Spring MVC、Spring Security)无缝集成,共享其配置和功能。我觉得也就是 stomp 的方式,因为我看一些文章中提到了这个 stomp 的名称。
2.1.添加依赖
1 | <dependency> |
2.2.Jakarta EE 规范
(1) 开发 ServerEndpoint 端点
@ServerEndpoint:将目前的类定义成一个websocket服务器端,注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
@OnOpen:当WebSocket建立连接成功后会触发这个注解修饰的方法。
@OnClose:当WebSocket建立的连接断开后会触发这个注解修饰的方法。
@OnMessage:当客户端发送消息到服务端时,会触发这个注解修改的方法。
@OnError:当WebSocket建立连接时出现异常会触发这个注解修饰的方法。
(2) 配置 ServerEndpointExporter
定义好端点后,需要在配置类中通过定义 ServerEndpointExporter Bean 进行注册。
1 |
|
【1】.在 Spring Boot 中整合、使用 WebSocket 1.添加依赖。2.开发 ServerEndpoint 端点。3.配置 ServerEndpointExporter。 这个就是上面的方法。
【2】.Using WebSocket to build an interactive web application
【3】.springBoot使用webSocket的几种方式以及在高并发出现的问题及解决 1.第一种方式-原生注解(tomcat内嵌);2.第二种方式-Spring封装;3.第三种方式-TIO;4.第四种方式-STOMP。
2.3.spring-websocket方式
(1) 配置 WebSocket 配置类
@EnableWebSocketMessageBroker:用于开启stomp协议,这样就能支持@MessageMapping注解,类似于@requestMapping一样,同时前端可以使用Stomp客户端进行通讯;
registerStompEndpoints实现:主要用来注册端点地址、开启跨域授权、增加拦截器、声明SockJS,这也是前端选择SockJS的原因,因为spring项目本身就支持;
configureMessageBroker实现:主要用来设置客户端订阅消息的路径(可以多个)、点对点订阅路径前缀的设置、访问服务端@MessageMapping接口的前缀路径、心跳设置等;
1 |
|
(2) 消息接口
消息接口使用@MessageMapping注解,前面讲的配置类@EnableWebSocketMessageBroker注解开启后才能使用这个。
1 |
|
【1】.【Java分享客栈】SpringBoot整合WebSocket+Stomp搭建群聊项目 这个也是上面的方法
【2】.Spring Boot 中的 STOMP 是什么,原理,如何使用 在 Spring Boot 中,STOMP 是一种简单的文本协议,用于在客户端和服务器之间进行实时消息传递。它是 WebSocket 协议的一种扩展,可以在 WebSocket 上运行。STOMP(Simple Text Oriented Messaging Protocol)是一种简单的文本协议,用于在客户端和服务器之间进行实时消息传递。它是一种基于文本的协议,易于理解和使用。STOMP 是 WebSocket 协议的一种扩展,可以在 WebSocket 上运行。在 Spring Boot 中,STOMP 是通过 Spring WebSocket 模块来实现的。在 Spring Boot 中,STOMP 是通过 WebSocket 进行实现的。WebSocket 是一种双向通信协议,可以在客户端和服务器之间建立持久化的连接。在 WebSocket 连接建立后,客户端和服务器之间可以通过发送和接收消息来进行实时通信。
【3】.Springboot 整合 WebSocket ,使用STOMP协议 ,前后端整合实战 (一) 1.后端整合websocket (STOMP协议);2.群发、指定单发;3.前端简单页面示例(接收、发送消息),前端页面也用了 socketjs。
3.单点推送
单点推送的含义就是说只针对某些应用进行推送,其他的一些应用不进行推动,也就是 一对一 的进行推送。
【1】.Springboot+websocket 单点推送
4.认证
有两种认证授权的方式,一种是使用连接地址,请求地址中带参数;一种是使用协议头.
4.1.基于请求地址
基于请求头的认证,就是将 token 携带到 websocket 的请求 url 中
(1)前端代码
1 | import { getToken } from '/@/utils/auth'; |
(2)后端代码
1 |
|
4.2.基于协议头的方式
基于协议头的方式,那就是讲认证信息放到了 header 的 Sec-WebSocket-Protocol 中。
(1)前端代码
1 | const token = (getToken() || '') as string; |
(2)后端代码
如果基于协议头传递了参数,后端响应的时候header也需要带上协议头参数。
1 |
|
【1】.headers.put(“token”
【2】.Spring中的Websocket身份验证和授权
【3】.springboot+websocket认证授权思路 创建自定义拦截器,实现HandshakeInterceptor接口
【4】.websocket-js连接如何携带token验证 1.请求地址中带参数;2.基于协议头:websocket请求头中可以包含Sec-WebSocket-Protocol这个属性,该属性是一个自定义的子协议。它从客户端发送到服务器并返回从服务器到客户端确认子协议。我们可以利用这个属性添加token。如果基于协议头传递了参数,后端响应的时候header也需要带上协议头参数。
【5】.WebSocket的集成 JEECG BOOT 增加websocket 旨在服务端主动向客户端推送数据,实现系统向在线用户推送消息,可群发,可对指定用户发送
【6】.websocket增加鉴权整合springboot 这里基于协议的授权,就是在 header 中增加了一个 Sec-WebSocket-Protocol 头。
5.wss
除了使用 nginx 的配置,在测试的时候,如何启动 wss 呢?
在我本地测试 https 或者 wss,就需要先配置,不用通过 nginx 等工具。
(1) 生成签名
1 | keytool -genkeypair -alias tomcat -keyalg RSA -keystore D:\keystore.jks |
(2) 配置
1 | server: |
【1】.Springboot 启用 https 安全协议 1.生成证书;2.导入证书;3.配置文件.
【2】.SpringBoot项目配置SSL后,WebSocket连接失败的解决方案
【3】.记一次网站全站http升级为https的过程,websocket : ws升级为wss遇到的问题等
【4】.2.WebSocketConfig配置
【5】.【Spring Boot】WebSocket 的 6 种集成方式
问题
1.SpringCloudAlibaba 应用webSocket,解决连接成功后会立刻断开等问题
【尝试方案】
(1)我尝试了修改配置,去掉认证,无效
(2)尝试了连接 jeecgboot 的websocket ,结果不行
(3)认证的时候携带了 token, 也绕过了 shiro,不行
(4)尝试找出错误,但是连报错都没有,什么原因都不知道
(5)去掉了 registry.addEndpoint(“/dronews”).setAllowedOriginPatterns(“*”).setHandshakeHandler(handshakeHandler) 中的 handshakeHandler 问题。
(6)后来发现了有新的错误提示了: Closing session due to exception for StandardWebSocketSession[id=9374f3ce-119f-693c-a327-a730435bc74d, uri=ws://localhost:8080/hjkj/dronews]java.lang.NullPointerException: Cannot invoke “java.security.Principal.getName()” because “principal” is null
(7)后来我在 在线测试 上测试我本地的 websocket 地址,好像是可以的,可以直连,不断开。那么问题就出在了我的客户端上了。
【解决方案】
经过我上面的不断的尝试,最后终于找到了原因,那就是是我的配置问题。我本来用了 websocket 的 token 的方式进行,但是我也同时用了协议的方式,增加了 [token], 结果就出现了问题。
1 | // 错误配置 |
【1】.SpringCloudAlibaba 应用webSocket,解决连接成功后会立刻断开等问题 权限认真把子路径也要加上去
【2】.【解决】websocket ws连不上或无法连接 因为我写的是@ServerEndpoint的方式。需要注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint。要注意,如果使用独立的servlet容器,而不是直接使用springboot的内置容器,就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。
【3】.Websocket 总是断开重连
【4】.SpringBoot集成websocket时出现的异常断开问题
【5】.java websocket 断线_WebSocket断开原因分析,再也不怕为什么又断开了 这里写了状态码,也就是断开的原因
【6】.WebSocket连接失败的原因与解决方案
【7】.java springboot websocket 服务 服务器主动关闭连接 导致 抛出java.io.EOFException异常
【8】.【websocket】spring boot 集成 websocket 的四种方式 1. 原生注解;2. Spring封装;3. TIO;4.STOMP
【9】.WebSocket之Sec-WebSocket-Protocol (带token发起连接) protocols对应的就是发起ws连接时, 携带在请求头中的Sec-WebSocket-Protocol属性, 服务端可以获取到此属性的值用于通信逻辑(即通信子协议,当然用来进行token认证也是完全没问题的)
2.连接总是断开
本地测试没有问题,使用nginx部署之后,配置了 wss,结果总是断开重连的情况。
【尝试方案】
(1)我检查了我的websocket链接,服务端打印了,已经能够建立连接了,但是最后还是没有连接上。
(2)我尝试输出了断开的错误码,结果显示 1006,还是服务器关闭了,到底为什么关闭了,我不晓得,除了配置文件不同之外,其他的没有什么不同的啊,我已经反反复复的检查了相关的配置。
(3)尝试测试prod这个配置文件,结果在本地的时候,这个配置文件没有问题,可以正常启动
(4)尝试将 yml 配置文件包含到主模块中,这个也没有用,我看了jar文件,打包后到模块里面包含了子模块的配置文件
(5)尝试通过frp将远程端口引导到本地开发环境进行测试,结果显示开始能建立连接,但是过后就直接断开了
(6)我尝试将 https 转成了 http,结果也还是不行
(7)我尝试直接通过 ip+端口 的方式,直接访问 websocket 服务,不通过nginx。刚开始总是出现各种莫名其妙的问题,还是不行,后来我在本地测试的时候,解决了各种个样的问题,最后到也还是能连通了。现在最大的问题,就是落在了那个 nginx 配置上面。
(8)尝试修改nginx配置,对着网上的东西,一点点的对,一点点的对。
(9)我一直以为是我配置了rtmp之后,导致的问题,后来发现去掉之后,还是不行。
【总结问题】
经过长时间的测试,总结
(1)使用 http 协议,ws 协议,不通过nginx代理,直接访问应用程序暴漏的端口,可以建立 websocket 连接。
(2)同样使用 http 协议,ws 协议,通过nginx代理后,访问应用程序暴漏的端口,结果就无法建立 websocket 连接。
(3)上面两部的 应用程序都是同一个,端口也都相同,没有修改,既然出现这样的情况,那么就有可能是 nginx 配置不正确。
【解决方案】
经过我长达一天的努力解决,最后竟然发现问题,修改前的nginx配置
1 | location /hjkj/ { |
修改后的 nginx如下,内容其实差不多。
1 | location /hjkj/ { |
总结,就是非常的扯淡,我足足浪费了一天的时间,但是解决问题的过程却特别的曲折,真是令人难过。
【1】.webSocket配置wss访问
【2】.WebSocket协议状态码1006:探索与解析 状态码1006表示连接已关闭,无法建立连接。具体含义如下:1.当客户端或服务器在建立WebSocket连接时发生异常,连接将被关闭,状态码1006将被返回。2.通常情况下,状态码1006表示网络连接中断、服务器宕机或其他无法建立连接的错误。与其他状态码相比,状态码1006的特点是无法建立连接,通常是由于网络问题或服务器故障引起的。解决状态码1006的常见方法包括:检查网络连接是否正常、检查服务器是否正常运行、检查服务器配置是否正确。
【3】.Nginx代理webSocket经常中断的解决方案, 如何保持长连接 nginx等待你 第一次通讯和第二次通讯的时间差,超过了它设定的最大等待时间,简单来说就是,超时,所以就啪的一声断了,开始上解决步骤。
【4】.Nginx配置WebSocket反向代理 这里我照着一点点的配置,最后还是找到了问题的所在
3.1015问题
在我使用 springboot 配置了 https 之后,由 ws 改为 wss,结果测试的时候,总是出现:1015 TLS_HANDSHAKE
【1】.websocket在线测试