您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

SpringBoot整合WebSocket详解

时间:2023-08-09 14:12:01  来源:  作者:Spring全家桶

环境:Springboot3.0.5

WebSocket介绍

WebSocket协议RFC 6455提供了一种标准化的方式,通过一个TCP连接在客户端和服务器之间建立全双工、双向的通信通道。它是一个不同于HTTP的TCP协议,但设计为在HTTP之上工作,使用80和443端口,并允许重用现有的防火墙规则。

WebSocket交互开始于一个HTTP请求,使用HTTP Upgrade header进行升级,在本例中是切换到WebSocket协议。下面的例子展示了这种交互:

GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket             // ①
Connection: Upgrade           // ②
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080

①:Upgrade header头部信息

②:使用 Upgrade 连接

支持WebSocket的服务器会返回类似下面的输出,而不是通常的200状态码:

HTTP/1.1 101 Switching Protocols 
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp

握手成功后,HTTP upgrade请求的TCP套接字保持打开,客户端和服务器可以继续发送和接收消息。

如果WebSocket服务器运行在web服务器(例如Nginx)后面,你可能需要配置它来将WebSocket升级请求传递给WebSocket服务器。同样,如果应用程序运行在云环境中,请查看云提供商提供的有关WebSocket支持的说明。

HTTP与WebSocket

尽管WebSocket在设计上是与HTTP兼容的,而且从HTTP请求开始,但重要的是要明白,这两种协议导致了非常不同的架构和应用程序编程模型。

在HTTP和REST中,应用程序被建模为多个url。为了与应用程序交互,客户端以请求-响应的方式访问这些url。服务器根据HTTP URL、方法和首部将请求路由到适当的处理程序。

相比之下,在websocket中,初始连接通常只有一个URL。随后,所有应用程序消息都在同一个TCP连接上流动。这是一种完全不同的异步、事件驱动的消息传递架构。

WebSocket也是一种底层传输协议,与HTTP不同,它对消息内容没有任何语义规定。这意味着除非客户端和服务器在消息语义上达成一致,否则无法路由或处理消息。

WebSocket客户端和服务器可以通过HTTP握手请求的Sec-WebSocket-Protocol头部来协商使用更高级别的消息传递协议(例如STOMP)。在这种情况下,他们需要制定自己的惯例。

什么时候该使用WebSocket

WebSockets可以使网页具有动态性和交互性。然而,在许多情况下,Ajax和HTTP流或长轮询的组合可以提供简单而有效的解决方案。

例如,新闻、邮件和社交源需要动态更新,但每隔几分钟更新一次完全没问题。另一方面,协作、游戏和金融应用需要更接近实时。

延迟本身并不是决定性因素。如果消息量相对较少(例如监视网络故障),HTTP流或轮询可以提供有效的解决方案。低延迟、高频率和高容量的组合才是WebSocket的最佳选择。

还要记住,在互联网上,你无法控制的限制性代理可能会阻止WebSocket交互,要么是因为它们没有配置为传递Upgrade header,要么是因为它们关闭了看起来空闲的长连接。这意味着对防火墙内的内部应用程序使用WebSocket比面向公众的应用程序更直接。

WebSocket核心API

Spring框架提供了一个WebSocket API,可以用它来编写处理WebSocket消息的客户端和服务器端应用程序。

  • WebSocketHandler

创建WebSocket服务器很简单,只需实现WebSocketHandler,或者扩展TextWebSocketHandler或BinaryWebSocketHandler。下面的例子使用了TextWebSocketHandler:

public class MessageHandler extends TextWebSocketHandler {


  @Override
  public void handleTextMessage(WebSocketSession session, TextMessage message) {
    System.out.printf("SessionId: %s, 接收到消息: %s%n", session.getId(), message.getPayload()) ;
    try {
      session.sendMessage(new TextMessage("服务端接收到消息 - " + message.getPayload())) ;
    } catch (IOException e) {
      e.printStackTrace();
    }
  }


  @Override
  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    System.out.printf("连接成功, 会话Id: %s, Attribute: %s%n", session.getId(), session.getAttributes()) ;
  }


  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    System.out.printf("连接关闭, 会话Id: %s, 关闭状态: %s%n", session.getId(), status.getCode() + " - " + status.getReason()) ;
  }


}

WebSocket配置

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {


  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(messageHandler(), "/message")
  }
  @Bean
  public WebSocketHandler messageHandler() {
    return new MessageHandler();
  }
}
  • WebSocket Handshake

要定制初始的HTTP WebSocket握手请求,最简单的方法是使用HandshakeInterceptor,它提供了握手前和握手后的方法。你可以使用这样的拦截器来阻止握手,或者让 WebSocketSession可以访问任何属性。下面的例子使用内置的拦截器将HTTP会话属性传递给WebSocket会话:

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {


  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry
      .addHandler(messageHandler(), "/message")
      .setHandshakeHandler(handshakeHandler())
      // 添加捂手拦截器
      .addInterceptors(new HandshakeInterceptor() {
        // 如果该方法返回false,则不允许建立连接
        @Override
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
            WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
          // todo
          attributes.put("uid", uid) ;
          return true ;
        }
        @Override
        public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception exception) {
          // todo
        }
      }) ;
  }
}
  • 部署

Spring WebSocket API很容易集成到Spring MVC应用程序中,DispatcherServlet可以同时处理HTTP WebSocket握手和其他HTTP请求。调用
WebSocketHttpRequestHandler也很容易集成到其他HTTP处理场景中。这样既方便又容易理解。但是,对于JSR-356运行时,需要特别注意。

JAVA WebSocket API (JSR-356)提供两种部署机制。第一种方法涉及启动时的Servlet容器类路径扫描(Servlet 3特性)@ServerEndpoint。另一个是Servlet容器初始化时使用的注册 API(ServletContAInerInitializer)。这两种机制都不可能对所有HTTP处理使用单个“前端控制器” — 包括WebSocket握手和所有其他HTTP请求 — 如Spring MVC的DispatcherServlet。

这是JSR-356的一个重要限制,Spring的WebSocket支持通过特定于服务器的RequestUpgradeStrategy实现来解决这个问题,即使运行在JSR-356运行时也是如此。Tomcat、Jetty、GlassFish、WebLogic、WebSphere和Undertow(以及WildFly)目前都存在这样的策略。

  • 服务配置

每个底层WebSocket引擎都公开了控制运行时特征的配置属性,例如消息缓冲区大小、空闲超时等。

对于Tomcat、WildFly和GlassFish,可以在WebSocket Java配置中添加
ServletServerContainerFactoryBean,如下面的例子所示:

@Bean
public ServletServerContainerFactoryBean servletServerContainerFactoryBean() {
  ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean() ;
  container.setMaxTextMessageBufferSize(8192) ;
  container.setMaxBinaryMessageBufferSize(8192) ;
  return container ;
}

 

  • 允许的来源

从Spring Framework 4.1.5开始,WebSocket和SockJS的默认行为是只接受同源请求。也可以允许所有或指定的来源列表。这个检查主要是为浏览器客户端设计的。没有什么能阻止其他类型的客户端修改Origin首部值。

三种可能的行为是:

  1. 仅允许同源请求(默认):在这种模式下,当启用SockJS时,Iframe HTTP响应头X-Frame-Options设置为SAMEORIGIN,并且禁用JSONP传输,因为它不允许检查请求的来源。因此,启用此模式时,不支持IE6和IE7。
  2. 允许指定的来源列表:每个允许的来源必须以http://或https://.开头在此模式下,当启用SockJS时,禁用IFrame传输。因此,启用此模式时,将不支持IE6到IE9。
  3. 允许所有来源:要启用此模式,你应该提供*作为允许的来源值。在该模式下,所有传输通道都可用。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {


  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry
      .addHandler(messageHandler(), "/message")
      .setAllowedOriginPatterns("*") ;
  }
}

测试

通过上面的介绍和配置,WebSocket环境就算是简单的配置完成了,接下来通过Postman进行测试。

图片图片

连接成功

发送消息及接收消息发送消息及接收消息

 

服务端接收到消息服务端接收到消息

 



Tags:SpringBoot   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  Search: SpringBoot  点击:(10)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19  Search: SpringBoot  点击:(86)  评论:(0)  加入收藏
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: SpringBoot  点击:(90)  评论:(0)  加入收藏
公司用了六年的 SpringBoot 项目部署方案,稳得一批!
本篇和大家分享的是springboot打包并结合shell脚本命令部署,重点在分享一个shell程序启动工具,希望能便利工作。 profiles指定不同环境的配置 maven-assembly-plugin打发布压...【详细内容】
2024-01-10  Search: SpringBoot  点击:(163)  评论:(0)  加入收藏
简易版的SpringBoot是如何实现的!!!
SpringBoot作为目前最流行的框架之一,同时是每个程序员必须掌握的知识,其提供了丰富的功能模块和开箱即用的特性,极大地提高了开发效率和降低了学习成本,使得开发人员能够更专注...【详细内容】
2023-12-29  Search: SpringBoot  点击:(132)  评论:(0)  加入收藏
用 SpringBoot+Redis 解决海量重复提交问题
前言 一:搭建redis的服务Api 二:自定义注解AutoIdempotent 三:token创建和检验 四:拦截器的配置 五:测试用例 六:总结前言:在实际的开发项目中,一个对外暴露的接口往往会面临很多...【详细内容】
2023-12-20  Search: SpringBoot  点击:(53)  评论:(0)  加入收藏
SpringBoot中如何优雅地个性化定制Jackson
当使用 JSON 格式时,Spring Boot 将使用ObjectMapper实例来序列化响应和反序列化请求。在本教程中,我们将了解配置序列化和反序列化选项的最常用方法。一、默认配置默认情况下...【详细内容】
2023-12-20  Search: SpringBoot  点击:(132)  评论:(0)  加入收藏
springboot-如何集成Validation进行参数校验
一、步骤概览 二、步骤说明1.引入依赖包在 pom.xml 文件中引入 validation 组件,它提供了在 Spring Boot 应用程序中进行参数校验的支持。<!-- WEB 程序依赖包 --><dependen...【详细内容】
2023-12-13  Search: SpringBoot  点击:(156)  评论:(0)  加入收藏
优雅的springboot参数校验,你学会了吗?
前言在后端的接口开发过程,实际上每一个接口都或多或少有不同规则的参数校验,有一些是基础校验,如非空校验、长度校验、大小校验、格式校验;也有一些校验是业务校验,如学号不能重...【详细内容】
2023-11-29  Search: SpringBoot  点击:(199)  评论:(0)  加入收藏
Springboot扩展点之BeanDefinitionRegistryPostProcessor,你学会了吗?
前言通过这篇文章来大家分享一下,另外一个Springboot的扩展点BeanDefinitionRegistryPostProcessor,一般称这类扩展点为容器级后置处理器,另外一类是Bean级的后置处理器;容器级...【详细内容】
2023-11-27  Search: SpringBoot  点击:(174)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(5)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(12)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(8)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(10)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(8)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(6)  评论:(0)  加入收藏
站内最新
站内热门
站内头条