WebSocket 协议简介

WebSocket 协议提供了一种标准化的方式,在客户端和服务端建立在一个TCP 连接之上的全双工,双向通信的协议。

WebSocket 交互开始于 HTTP 请求,使用 HTTP 请求的 header 中的 Upgrade 进行切换到 WebSocket 协议。

HTTP 和 WebSocket 对比

即使 WebSocket 的设计兼容 HTTP 协议和开始于 HTTP 请求,了解两个协议是不同架构和应用模型是非常重要的。

在 HTTP 和 REST, 一个应用有多个URL, 对于交互的应用,客户端可以访问这些 URL,请求和响应的风格。服务端会根据 URL、http method、header 等等信息进行路由处理。

相比之下, WebSocket 协议是使用一个URL 初始化连接。应用所有的消息通过同一个 TCP 通信,这一点是完全不同的异步、事件驱动的消息传递体系结构。

WebSocket API 的使用

  1. Webflux 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  1. 定义一个Handler,用于处理数据的请求和响应
import java.time.Duration;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; public class MyWebSocketHandler implements WebSocketHandler {
// 每间隔 1 秒,数字加 1
private Flux<Long> intervalFlux = Flux.interval(Duration.ofSeconds(1L), Duration.ofSeconds(1L)); @Override
public Mono<Void> handle(WebSocketSession session) {
return session.send(intervalFlux.map(item -> session.textMessage(item + "")))
.and(session.receive().map(WebSocketMessage::getPayloadAsText).log());
}
}
  1. 配置类,配置 WebSocket 的 URL 信息
@Configuration
public class WebConfig { @Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
} @Bean
public HandlerMapping handlerMapping() {
Map<String, WebSocketHandler> urlMap = new HashMap<>();
urlMap.put("/path", new MyWebSocketHandler());
int order = -1;
return new SimpleUrlHandlerMapping(urlMap, order);
}
}
  1. 客户端
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import reactor.core.publisher.Mono; public class Client { public static void main(String[] args) throws URISyntaxException { WebSocketClient client = new ReactorNettyWebSocketClient();
URI url = new URI("ws://localhost:8080/path");
client.execute(url,
session -> session.send(Mono.just(session.textMessage("hello world")))
.thenMany(session.receive().map(WebSocketMessage::getPayloadAsText).log())
.then())
.block(Duration.ofSeconds(10));
}
}

服务端日志

2021-08-10 22:42:36.162  INFO 85792 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2021-08-10 22:42:36.168 INFO 85792 --- [ main] c.e.s.SpringBootDemoApplication : Started SpringBootDemoApplication in 3.583 seconds (JVM running for 4.462)
2021-08-10 22:42:51.518 INFO 85792 --- [ctor-http-nio-2] reactor.Flux.Map.1 : onSubscribe(FluxMap.MapSubscriber)
2021-08-10 22:42:51.522 INFO 85792 --- [ctor-http-nio-2] reactor.Flux.Map.1 : request(unbounded)
2021-08-10 22:42:51.534 INFO 85792 --- [ctor-http-nio-2] reactor.Flux.Map.1 : onNext(hello world)
2021-08-10 22:43:00.956 INFO 85792 --- [ctor-http-nio-2] reactor.Flux.Map.1 : onComplete()

客户端日志

22:42:51.527 [reactor-http-nio-1] DEBUG reactor.netty.channel.FluxReceive - [id: 0xd95be56c, L:/127.0.0.1:50773 - R:localhost/127.0.0.1:8080] Subscribing inbound receiver [pending: 0, cancelled:false, inboundDone: false]
22:42:51.530 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onSubscribe(FluxMap.MapSubscriber)
22:42:51.531 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - request(unbounded)
22:42:52.518 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(0)
22:42:53.513 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(1)
22:42:54.518 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(2)
22:42:55.513 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(3)
22:42:56.514 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(4)
22:42:57.516 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(5)
22:42:58.518 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(6)
22:42:59.512 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(7)
22:43:00.513 [reactor-http-nio-1] INFO reactor.Flux.Map.1 - onNext(8)
22:43:00.947 [main] INFO reactor.Flux.Map.1 - cancel()
22:43:00.948 [reactor-http-nio-1] DEBUG reactor.netty.http.client.HttpClientOperations - [id: 0xd95be56c, L:/127.0.0.1:50773 - R:localhost/127.0.0.1:8080] Cancelling Websocket inbound. Closing Websocket
Exception in thread "main" java.lang.IllegalStateException: Timeout on blocking read for 10000 MILLISECONDS

至此,Spring WebFlux 中 WebSocket 协议使用的结束了。

Spring WebFlux 基础教程:WebSocket 使用的更多相关文章

  1. Spring WebFlux 基础教程:参数校验

    请求参数校验,在实际的应用中很常见,网上的文章大部分提供的使用注解的方式做参数校验.本文主要介绍 Spring Webflux Function Endpoint 使用 Spring Validati ...

  2. Spring Boot 基础教程系列学习文档

    Spring Boot基础教程1-Spring Tool Suite工具的安装 Spring Boot基础教程2-RESTfull API简单项目的快速搭建 Spring Boot基础教程3-配置文件 ...

  3. Spring Cloud基础教程

    Spring Cloud基础教程  2017-04-04 被围观 90375 次 该教程内容不定时更新,如您对这些内容感兴趣,可以关注我的博客或微信公众号! 本教程示例代码: GitHub:https ...

  4. Spring Boot基础教程》 第1节工具的安装和使用

    <Spring Boot基础教程> 第1节 工具的安装和使用 Spring Boot文档 https://qbgbook.gitbooks.io/spring-boot-reference ...

  5. 经典Spring入门基础教程详解

    经典Spring入门基础教程详解 https://pan.baidu.com/s/1c016cI#list/path=%2Fsharelink2319398594-201713320584085%2F ...

  6. Spring Cloud基础教程视频教程

    视频课程包含: Spring Cloud基础视频教程24G 目录 获取方式: 关注公众微信号:博涵大数据 或者扫描下面的二维码关注获取. 关注后在公众平台上回复"SpringCloud基础& ...

  7. Spring Cloud Alibaba基础教程:Nacos的集群部署

    继续说说生产环境的Nacos搭建,通过上一篇<Spring Cloud Alibaba基础教程:Nacos的数据持久化>的介绍,我们已经知道Nacos对配置信息的存储原理,在集群搭建的时候 ...

  8. Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则

    上一篇我们介绍了如何通过Nacos的配置功能来存储限流规则.Apollo是国内用户非常多的配置中心,所以,今天我们继续说说Spring Cloud Alibaba Sentinel中如何将流控规则存储 ...

  9. Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则

    通过上一篇<使用Sentinel实现接口限流>的介绍,相信大家对Sentinel已经有了初步的认识.在Spring Cloud Alibaba的整合封装之下,接口限流这件事情可以非常轻易的 ...

随机推荐

  1. Unity Lamba错误集

    请先看对象层级视图 /// <summary> /// 选择关卡的内容对象 /// </summary> public Transform Transform_levelCon ...

  2. Kubernetes自动横向伸缩集群节点以及介绍PDB资源

    在kubernetes中,有HPA在需要的时候创建更多的pod实例.但万一所有的节点都满了,放不下更多pod了,怎么办?显然这个问题并不局限于Autoscaler创建新pod实例的场景.即便是手动创建 ...

  3. 22、编译安装nginx及性能优化

    22.1.编译安装nginx: 1.下载nginx: [root@slave-node1 ~]# mkdir -p /tools/ [root@slave-node1 ~]# cd /tools/ [ ...

  4. 八皇后O(1)算法题解

    题目描述 在国际象棋棋盘上(8*8)放置八个皇后,使得任意两个皇后之间不能在同一行,同一列,也不能位于同于对角线上.问共有多少种不同的方法,并且按字典序从小到大指出各种不同的放法. 题解 见证奇迹的时 ...

  5. yoyogo v1.7.4 发布,支持 grpc v1.3.8 & etcd 3.5.0

    YoyoGo (Go语言框架)一个简单.轻量.快速.基于依赖注入的微服务框架( web .grpc ),支持Nacos/Consoul/Etcd/Eureka/k8s /Apollo等 . https ...

  6. [心得体会]RabbitMQ

    RabbitMQ是什么? 消息队列, 基于AMQP(高级消息队列), 使用Erlang语言编写, 收发消息使用 有什么用? 有什么应用场景? 1. 任务异步处理 2. 应用程序解耦 为什么使用Rabb ...

  7. MySQL 插入中文后,显示为空白

    https://blog.csdn.net/sun_hj_95/article/details/79488583 在MySQL中插入中文后,显示如下: 解决方案: 在my.ini (在MySQL的安装 ...

  8. Adaptive AUTOSAR 学习笔记 5 - 架构 - 物理视图

    本系列学习笔记基于 AUTOSAR Adaptive Platform 官方文档 R20-11 版本 AUTOSAR_EXP_PlatformDesign.pdf 缩写 AP:AUTOSAR Adap ...

  9. Django基础-04篇 Django开发前后端联动

    1. 写views views.py代码块 1.在前端以/article/{{ article.id }}这种方式请求后台, 参数配置在urls.py中path('category/<int:i ...

  10. C++ 标准模板库(STL)——迭代器(iterators)的用法及理解

    C++ STL中迭代器(iterators)用于遍历对象集合的元素.由于容器大小随着插入删除等操作动态改变,无法像静态数组那样获取数组长度然后遍历容器里的所有元素:这时就需要迭代器,每次从容器内第一个 ...