Netty WebSocket 拆包浅析

如题所述

第1个回答  2022-06-26
最近项目中运用 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  规范进行相应实现,可以实现无缝对接。

参考: Netty做webSocket客户端,服务端拆包发送客户端接收处理