Netty是针对Java Nio(非阻塞式)Java网络API,隐藏背后复杂性(可靠、可扩展的事件处理器处理和调度数据,并尽可能的保证有效、正确性和安全性),提供易于使用的服务器\客户端网络编程框架。
最近项目中运用 WebSocket 的场景越来越多,自然而然。踩到的坑也就越来越多,例如最经常遇到的拆包,粘包问题。而当发现这个问题的时候,起初我认为与我之前通过 mina 实现自定义协议头,解决 socket 进行数据传输时遇到拆包问题的场景是一样的。而当我慢慢深入了解 WebSocket的时候才发现,实际上,WebSocket 的出现,就是为了解决拆包,粘包的问题的。因为这些事情,本身就是应该由运用层去解决,而不是在TCP/IP层解决。
通过网络搜索,我们可以知道 WebSocket 协议是根据 RFC6455 规范实现的,而最新版本是13(距离现今也接近10年了)。
以下是 WebScoket 定义的协议帧片段。
通过以上的帧定义片段。我也就明白了,为什么说 WebSocket 是为了解决拆包,粘包问题等一系列应用层问题而诞生的。从 RCF6455 规范定义第5.3章节中可以发现,每次发送或者接收的帧报文中,都会有定义协议的一些信息,例如头部大小,数据大小,以及帧类型,标识位等信息。
看完协议定义,那么开始查看 Netty 源码。
以下是 Netty 定义的相关帧类型以供开发者使用。
当服务端接收到协议由 HTTP 握手升级协议 WebSocket 时,我们需要通过 WebSocketServerHandshakerFactory 类新建 WebSocketHandshaker,此时会判断 WebSocket 协议版本以及相关信息进行校验。
而 WebSocketServerHandshaker 中,分别定义了 WebSocketFrameDecoder,WebSocketFrameEncoder 进行解码,编码调用。
那么从现实开始,大概也就清楚了,Netty 中 WebSocket 和 Socket 实现,实际上都是一样的,都是一个解码器(负责接收数据,处理成需要的类型,例如文本,二进制),一个编码器(负责根据协议版本,进行帧封装)的结构。
从 WebSocket13FrameDecoder 我们可以得知,实际上 WebSocket13FrameDecoder 是继承自 WebSocket08FrameDecoder(由于实现细节一致),而 WebSocket08FrameDecoder 是继承自 ByteToMessageDecoder ,实际上都是通过二进制数据进行解码处理的。
查看 WebSocket08FrameDecoder 我们可以了解到具体的实现细节,就是每次解码的时候,都会读取首部信息,然后依次对数据进行处理。而拆包等操作,都是已经进行的相应处理和封装。
看完 Netty 的源码实现,那么就可以进行实际的编码解决问题了。
当服务端/客户端发现包文过大时,会进行拆包。而为每个包定义一系列的定义。
例如:当接收一个 Text 消息时,Netty 首先会实例化一个 TextWebSocketFrame 对象并传递给调用方,而通过 isFinalFragment 我们可以判断出,这个帧对象是否已经传输完毕,如果传输完毕,那么进行业务处理。如果没有传输完毕,那么继续等待余下信息,进行拼接处理。
以上为服务端接收拆包信息的处理方式,反之亦然,客户端接收消息也可以进行相应操作。而根据 WebSocket 1.3 版本实现的组件,也都根据 RFC6455 规范进行相应实现,可以实现无缝对接。
网友评论