写点什么

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/


最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 2
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
netty系列之:使用netty搭建websocket客户端