第08课:WebFlux 中 WebSocket 实现通信

前言

WebFlux 该模块中包含了对反应式 HTTP、服务器推送事件和 WebSocket 的客户端和服务器端的支持。这里我们简单实践下 WebFlux 中 WebSocket 实现通信。

什么是 WebSocket

WebSocket 是一种通信协议,类比下 HTTP 协议,HTTP 协议只能有客户端发起请求,然后得到响应。 一般通过 HTTP 的轮询方式,实现 WebSocket 类似功能。

因为轮询,每次新建连接,请求响应,浪费资源。WebSocket 就出现了,它支持客户端和服务端双向通讯。类似 http 和 https,WebSocket 的标识符为 ws 和 wss,案例地址为:

ws://localhost:8080/echo

结构

回到这个工程,新建一个工程编写 WebSocket 实现通信案例。工程如图:

目录核心如下:

  • EchoHandler websocket 处理类(类似 HTTP Servlet 处理)
  • WebSocketConfiguration websocket 配置类
  • websocket-client.html HTML 客户端实现
  • WSClient java 客户端实现

单击这里查看源代码。

EchoHandler 处理类

代码如下:

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Mono; @Component
public class EchoHandler implements WebSocketHandler {
@Override
public Mono<Void> handle(final WebSocketSession session) {
return session.send(
session.receive()
.map(msg -> session.textMessage(
"服务端返回:小明, -> " + msg.getPayloadAsText())));
}
}

代码详解:

  • WebSocketHandler 接口,实现该接口来处理 WebSokcet 消息。
  • handle(WebSocketSession session) 方法,接收 WebSocketSession 对象,即获取客户端信息、发送消息和接收消息的操作对象。
  • receive() 方法,接收消息,使用 map 操作获取的 Flux 中包含的消息持续处理,并拼接出返回消息 Flux 对象。
  • send() 方法,发送消息。消息为“服务端返回:小明, -> ”开头的。

WebSocketConfiguration 配置类

代码如下:

@Configuration
public class WebSocketConfiguration { @Autowired
@Bean
public HandlerMapping webSocketMapping(final EchoHandler echoHandler) {
final Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/echo", echoHandler); final SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
mapping.setUrlMap(map);
return mapping;
} @Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}

代码详解:

  • WebSocketHandlerAdapter 负责将 EchoHandler 处理类适配到 WebFlux 容器中;
  • SimpleUrlHandlerMapping 指定了 WebSocket 的路由配置;
  • 使用 map 指定 WebSocket 协议的路由,路由为 ws://localhost:8080/echo。

运行工程

一个操作 Redis 工程就开发完毕了,下面运行工程验证下。使用 IDEA 右侧工具栏,点击 Maven Project Tab,点击使用下 Maven 插件的 install 命令。或者使用命令行的形式,在工程根目录下,执行 Maven 清理和安装工程的指令:

cd springboot-webflux-8-websocket
mvn clean install

在控制台中看到成功的输出:

... 省略
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:30 min
[INFO] Finished at: 2018-10-15T10:00:54+08:00
[INFO] Final Memory: 31M/174M
[INFO] ------------------------------------------------------------------------

在 IDEA 中执行 Application 类启动,任意正常模式或者 Debug 模式,可以在控制台看到成功运行的输出:

... 省略
2018-04-10 08:43:39.932 INFO 2052 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-04-10 08:43:39.935 INFO 2052 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
2018-04-10 08:43:39.960 INFO 2052 --- [ main] org.spring.springboot.Application : Started Application in 6.547 seconds (JVM running for 9.851)

打开 https://www.websocket.org/echo.html网页,大多数浏览器是支持 WebSokcet 协议的。

Location - 输入通信地址、点击 Conect 会出现 CONNECTED。

然后发送消息,可以看到服务端返回对应的消息。如果此时关闭了服务端,那么会出现 DISCONNECTED:

websocket-client.html HTML 客户端实现

实现 HTML 客户端:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Client WebSocket</title>
</head>
<body> <div class="chat"></div> <script>
var clientWebSocket = new WebSocket("ws://localhost:8080/echo"); clientWebSocket.onopen = function () {
console.log("clientWebSocket.onopen", clientWebSocket);
console.log("clientWebSocket.readyState", "websocketstatus");
clientWebSocket.send("你好!");
} clientWebSocket.onclose = function (error) {
console.log("clientWebSocket.onclose", clientWebSocket, error);
events("聊天会话关闭!");
} function events(responseEvent) {
document.querySelector(".chat").innerHTML += responseEvent + "<br>";
}
</script>
</body>
</html>

大多数浏览器是支持 WebSocket,代码详解如下:

  • 网页打开是,会调用 onopen 方法,并发送消息给服务端“你好!”;
  • 如果服务端关闭,会调用 onclose 方法,页面会出现“聊天会话关闭!”信息。

WSClient Java 客户端实现

类似,HTTPClient 调用 HTTP,WebSocket 客户端去调用 WebSokcet 协议,并实现服务。代码如下:

public class WSClient {
public static void main(final String[] args) {
final WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute(URI.create("ws://localhost:8080/echo"), session ->
session.send(Flux.just(session.textMessage("你好")))
.thenMany(session.receive().take(1).map(WebSocketMessage::getPayloadAsText))
.doOnNext(System.out::println)
.then())
.block(Duration.ofMillis(5000));
}
}

代码详解:

  • ReactorNettyWebSocketClient 是 WebFlux 默认 Reactor Netty 库提供的 WebSocketClient 实现。
  • execute 方法,与 ws://localhost:8080/echo 建立 WebSokcet 协议连接。
  • execute 需要传入 WebSocketHandler 的对象,用来处理消息,这里的实现和前面的 EchoHandler 类似。
  • 通过 WebSocketSession 的 send 方法来发送字符串“你好”到服务器端,然后通过 receive 方法来等待服务器端的响应并输出。

总结

这一篇内容主要一起实践了简单的 WebSocket 的应用操作,以及 WebSocket 客户端小例子。

工程:springboot-webflux-8-websocket

单击这里查看源代码。

Spring Boot WebFlux-08——WebFlux 中 WebSocket 实现通信的更多相关文章

  1. Spring Boot 监听 Activemq 中的特定 topic ,并将数据通过 RabbitMq 发布出去

    1.Spring Boot 和 ActiveMQ .RabbitMQ 简介 最近因为公司的项目需要用到 Spring Boot , 所以自学了一下, 发现它与 Spring 相比,最大的优点就是减少了 ...

  2. spring boot 项目从配置文件中读取maven 的pom.xml 文件标签的内容。

    需求: 将pom.xml 文件中的版本号读取到配置文件并打印到日志中. 第一步: 在pom.xml 中添加以下标签. 第二步: 将version 标签的值读取到配置文件中 这里使用 @@  而不是  ...

  3. 实战基于Spring Boot 2的WebFlux和mLab搭建反应式Web

    Spring Framework 5带来了新的Reactive Stack非阻塞式Web框架:Spring WebFlux.作为与Spring MVC并行使用的Web框架,Spring WebFlux ...

  4. Spring Boot 2.0 WebFlux 教程 (一) | 入门篇

    目录 一.什么是 Spring WebFlux 二.WebFlux 的优势&提升性能? 三.WebFlux 应用场景 四.选 WebFlux 还是 Spring MVC? 五.异同点 六.简单 ...

  5. Spring Boot实现STOMP协议的WebSocket

    关注公众号:锅外的大佬 每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长! WebSocket协议是应用程序处理实时消息的方法之一.最常见的替代方案是长轮询(long polling)和 ...

  6. Spring Boot事务管理(中)

    在上一篇 Spring Boot事务管理(上)的基础上介绍Spring Boot事务属性和事务回滚规则 . 4 Spring Boot事务属性 什么是事务属性呢?事务属性可以理解成事务的一些基本配置, ...

  7. Spring boot 直接访问templates中html文件

    application.properties 在浏览器中输入http://localhost:8080/index.html 会报一个 因为Spring boot 无法直接访问templates下的文 ...

  8. 【原】spring boot source 1.5 中不支持 diamond 运算符

    最近要开发新的项目,就花了几天时间看了下spring boot的相关资料,然后做了一个demo,不得不说开发效率确实很快,几行注解就完成了事务,aop,数据库等相关配置:但由于先前习惯了spring ...

  9. Spring Boot在反序列化过程中:jackson.databind.exc.InvalidDefinitionException cannot deserialize from Object value

    错误场景 用Spring boot写了一个简单的RESTful API,在测试POST请求的时候,request body是一个符合对应实体类要求的json串,post的时候报错. 先贴一段error ...

随机推荐

  1. MFC的六大机制

    MFC的六大机制 程序的初始化过程 运行时类型识别 动态创建 永久保存 消息映射 命令传递 运行时类型识别 MFC的运行时类型识别就是在程序运行过程中判断某个对象是否属于某个类,MFC通过为需要进行运 ...

  2. 北航OO(2020)第二单元博客作业

    第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...

  3. KMP板子(其实还没完全懂...)

    KMP模板 1.next数组的实际含义 next数组从-1开始,主串a,子串b,next[j]=k,满足b[0,k-1]==b[j-k,j-1],k同时也为b子串前缀的下标,j为b子串后缀的下标 ge ...

  4. Django(26)HttpResponse对象和JsonResponse对象

    HttpResponse对象 Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个HttpRequest对象传给视图函数.那么视图函数在处理完相关的逻辑后,也需要返回一个响 ...

  5. ES6学习-4 解构赋值(1)数组的解构赋值

    解构赋值是ES6很大的一个提升,为我们带来了很多方便,但用不好,会使程序的可读性变差,所以用时要注意,尽量保持程序的易读性. 数组解构赋值 在JS没有支持解构赋值之前,我们声明几个变量并赋值通常都是像 ...

  6. 5.配置IP

    静态IP配置 1.NAT模式设置 首先设置虚拟机中NAT模式的选项,打开VMware,点击"编辑"下的"虚拟网络编辑器",设置NAT参数 注意: VMware ...

  7. NPM包管理器入门(附加cnpm : 无法加载文件错误解决方案)

    NPM 包管理器 1.作用: 快速构建nodejs工程 快速安装和依赖第三个模块 2.使用方法 快速构建 npm init 会得到一package.json文件 { "name": ...

  8. [bug] Maven修改pom文件后jdk版本回退

    参考 https://www.cnblogs.com/wxgblogs/p/6696229.html

  9. [刷题] 19 Remove Nth Node From End of List

    要求 给定一个链表,删除倒数第n个节点 示例 1->2->3->4->5->NULL , n=2 1->2->3->5 边界 n是从0还是从1计 n不合 ...

  10. [转载]libvirt(virsh命令总结)

    libvirt(virsh命令总结) virsh回车进入交互式界面: version pwd hostname 显示本节点主机名 nodeinfo  显示节点信息 list --all 显示所有云主机 ...