netty系列之:使用netty搭建websocket客户端
简介
在网速快速提升的时代,浏览器已经成为我们访问各种服务的入口,很难想象如果离开了浏览器,我们的网络世界应该如何运作。现在恨不得把操作系统都搬上浏览器。但是并不是所有的应用都需要浏览器来执行,比如服务器和服务器之间的通信,就需要使用到自建客户端来和服务器进行交互。
本文将会介绍使用netty客户端连接websocket的原理和具体实现。
浏览器客户端
在介绍netty客户端之前,我们先看一个简单的浏览器客户端连接websocket的例子:
// 创建连接
const socket = new WebSocket('ws://localhost:8000');
// 开启连接
socket.addEventListener('open', function (event) {
socket.send('没错,开启了!');
});
// 监听消息
socket.addEventListener('message', function (event) {
console.log('监听到服务器的消息 ', event.data);
});
这里使用了浏览器最通用的语言javascript,并使用了浏览器提供的websocket API进行操作,非常的简单。
那么用netty客户端实现websocket的连接是否和javascript使用一样呢?我们一起来探索。
netty对websocket客户端的支持
先看看netty对websocket的支持类都有哪些,接着我们看下怎么具体去使用这些工具类。
WebSocketClientHandshaker
和websocket server一样,client中最核心的类也是handshaker,这里叫做WebSocketClientHandshaker。这个类有什么作用呢?一起来看看。
这个类主要实现的就是client和server端之间的握手。
我们看一下它的最长参数的构造类:
protected WebSocketClientHandshaker(URI uri, WebSocketVersion version, String subprotocol,
HttpHeaders customHeaders, int maxFramePayloadLength,
long forceCloseTimeoutMillis, boolean absoluteUpgradeUrl)
参数中有websocket连接的URI,像是:"ws://flydean.com/mypath"。
有请求子协议的类型subprotocol,有自定义的HTTP headers:customHeaders,有最大的frame payload的长度:maxFramePayloadLength,有强制timeout关闭的时间,有使用HTTP协议进行升级的URI地址。
怎么创建handshaker呢?同样的,netty提供了一个WebSocketClientHandshakerFactory方法。
WebSocketClientHandshakerFactory提供了一个newHandshaker方法,可以方便的创建各种不同版本的handshaker:
if (version == V13) {
return new WebSocketClientHandshaker13(
webSocketURL, V13, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis);
}
if (version == V08) {
return new WebSocketClientHandshaker08(
webSocketURL, V08, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis);
}
if (version == V07) {
return new WebSocketClientHandshaker07(
webSocketURL, V07, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis);
}
if (version == V00) {
return new WebSocketClientHandshaker00(
webSocketURL, V00, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis);
}
可以看到,根据传入协议版本的不同,可以分为WebSocketClientHandshaker13、WebSocketClientHandshaker08、WebSocketClientHandshaker07、WebSocketClientHandshaker00这几种。
WebSocketClientCompressionHandler
通常来说,对于webSocket协议,为了提升传输的性能和速度,降低网络带宽占用量,在使用过程中通常会带上额外的压缩扩展。为了处理这样的压缩扩展,netty同时提供了服务器端和客户端的支持。
对于服务器端来说对应的handler叫做WebSocketServerCompressionHandler,对于客户端来说对应的handler叫做WebSocketClientCompressionHandler。
通过将这两个handler加入对应pipline中,可以实现对websocket中压缩协议扩展的支持。
对于协议的扩展有两个级别分别是permessage-deflate和perframe-deflate,分别对应PerMessageDeflateClientExtensionHandshaker和DeflateFrameClientExtensionHandshaker。
至于具体怎么压缩的,这里就不详细进行讲解了, 感兴趣的小伙伴可以自行了解。
netty客户端的处理流程
前面讲解了netty对websocket客户端的支持之后,本节将会讲解netty到底是如何使用这些工具进行消息处理的。
首先是按照正常的逻辑创建客户端的Bootstrap,并添加handler。这里的handler就是专门为websocket定制的client端handler。
除了上面提到的WebSocketClientCompressionHandler,就是自定义的handler了。
在自定义handler中,我们需要处理两件事情,一件事情就是在channel ready的时候创建handshaker。另外一件事情就是具体websocket消息的处理了。
创建handshaker
首先使用WebSocketClientHandshakerFactory创建handler:
TestSocketClientHandler handler =
new TestSocketClientHandler(
WebSocketClientHandshakerFactory.newHandshaker(
uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
然后在channel active的时候使用handshaker进行握手连接:
public void channelActive(ChannelHandlerContext ctx) {
handshaker.handshake(ctx.channel());
}
然后在进行消息接收处理的时候还需要判断handshaker的状态是否完成,如果未完成则调用handshaker.finishHandshake方法进行手动完成:
if (!handshaker.isHandshakeComplete()) {
try {
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
log.info("websocket Handshake 完成!");
handshakeFuture.setSuccess();
} catch (WebSocketHandshakeException e) {
log.info("websocket连接失败!");
handshakeFuture.setFailure(e);
}
return;
}
当handshake完成之后,就可以进行正常的websocket消息读写操作了。
websocket消息的处理
websocket的消息处理比较简单,将接收到的消息转换成为WebSocketFrame进行处理即可。
WebSocketFrame frame = (WebSocketFrame) msg;
if (frame instanceof TextWebSocketFrame) {
TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
log.info("接收到TXT消息: " + textFrame.text());
} else if (frame instanceof PongWebSocketFrame) {
log.info("接收到pong消息");
} else if (frame instanceof CloseWebSocketFrame) {
log.info("接收到closing消息");
ch.close();
}
总结
本文讲解了netty提供的websocket客户端的支持和具体的对接流程,大家可以再次基础上进行扩展,以实现自己的业务逻辑。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/25-netty-websocket-client/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
netty系列之:使用netty搭建websocket客户端的更多相关文章
- netty系列之:使用netty搭建websocket服务器
目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...
- netty系列之:请netty再爱UDT一次
目录 简介 netty对UDT的支持 搭建一个支持UDT的netty服务 异常来袭 TypeUDT和KindUDT 构建ChannelFactory SelectorProviderUDT 使用UDT ...
- netty系列之: 在netty中使用 tls 协议请求 DNS 服务器
目录 简介 支持DoT的DNS服务器 搭建支持DoT的netty客户端 TLS的客户端请求 总结 简介 在前面的文章中我们讲过了如何在netty中构造客户端分别使用tcp和udp协议向DNS服务器请求 ...
- netty系列之:在netty中使用protobuf协议
目录 简介 定义protobuf 定义handler 设置ChannelPipeline 构建client和server端并运行 总结 简介 netty中有很多适配不同协议的编码工具,对于流行的goo ...
- netty系列之:在netty中处理CORS
目录 简介 服务端的CORS配置 CorsConfigBuilder CorsHandler netty对cors的支持 总结 简介 CORS的全称是跨域资源共享,他是一个基于HTTP-header检 ...
- netty系列之:使用netty实现支持http2的服务器
目录 简介 基本流程 CleartextHttp2ServerUpgradeHandler Http2ConnectionHandler 总结 简介 上一篇文章中,我们提到了如何在netty中配置TL ...
- netty系列之:在netty中使用native传输协议
目录 简介 native传输协议的依赖 netty本地传输协议的使用 总结 简介 对于IO来说,除了传统的block IO,使用最多的就是NIO了,通常我们在netty程序中最常用到的就是NIO,比如 ...
- Netty 系列目录
Netty 系列目录 二 Netty 源码分析(4.1.20) 1.1 Netty 源码(一)Netty 组件简介 2.1 Netty 源码(一)服务端启动 2.2 Netty 源码(二)客户端启动 ...
- netty系列之:netty架构概述
目录 简介 netty架构图 丰富的Buffer数据机构 零拷贝 统一的API 事件驱动 其他优秀的特性 总结 简介 Netty为什么这么优秀,它在JDK本身的NIO基础上又做了什么改进呢?它的架构和 ...
随机推荐
- jquery validate 如何校验多个相同name
在表单页中有如下代码 <form> <input name="zhai"/><!-- 三个相同name的input --> <input ...
- Javascript - Vue - webpack + vue-cil
cnpm(node package manager)和webpack模块 npm是运行在node.js环境下的包管理工具(先安装node.js,再通过命令 npm install npm -g 安装n ...
- C# 使用正则表达式替换PPT中的文本(附vb.net代码)
文本介绍如何在C#程序中使用正则表达式替换PPT幻灯片中的指定文本内容.具体操作步骤如下: 1. 在程序中引用Spire.Presentation.dll.两种方法可参考如下: (1)直接在程序中通过 ...
- 及上一篇linux安装mysql的说明
mysql8.0安全策略 1 密码规定:数字英文大小写加特殊符号组成(可以不按照规则,详情去百度设置) 2. mysql数据库用户密码字段不再是password 而是authentication_st ...
- 阿里云服务器,http免费转https详细教程
1.搜ssl证书,点击立即购买 2.这里我们选择免费证书,点击右边立即购买,去支付 购买完成之后,申请证书状态会显示在审核中,不过很快的,几分钟就申请成功了,以下就是我申请成功的界面,因为我服务器用的 ...
- Java的Class类及static块的执行时机
要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工程由Class对象完成,它包含了与类有关的信息.Java使用Class对象来执行其RTTI,即使你执行的是类似转型 ...
- Kafka详细教程加面试题
一.部署kafka集群 启动zookeeper服务: zkServer.sh start 修改配置文件config/server.properties #broker 的全局唯一编号,不能重复 bro ...
- Windows系统定时备份MySQL数据库
当一个网站投入使用时,定期备份数据库是必要的事.那么,在Windows系统上,我们该如何做呢? 如下语句可以实现备份及还原MySQL数据库: 备份MySQL数据库 mysqldump -uroot - ...
- Jetpack Compose学习(3)——图标(Icon) 按钮(Button) 输入框(TextField) 的使用
原文地址: Jetpack Compose学习(3)--图标(Icon) 按钮(Button) 输入框(TextField) 的使用 | Stars-One的杂货小窝 本篇分别对常用的组件:图标(Ic ...
- noip模拟46
A. 数数 排好序从两头贪心即可 B. 数树 首先很容易想到容斥 如果选择的边集的相关点集有点的度数大于 \(1\) 是不合法的 也就是说一定形成若干条长度不一的链 要给这些链上的点安排排列中的数,方 ...