结合ODL netconf、openflow、ovsdb协议及netty实现深入分析tcp沾包拆包问题

本文首先简单回顾了沾包拆包问题的本质,然后从odl目前应用最广泛的三个南向协议包括openflow、netconf和ovsdb,从代码实现出发,分析了odl如何来解决三个不同类型协议的沾包粘包问题,给出具体思路!

1 tcp沾包粘包问题的本质

1.1 tcp报文发送过程

首先回顾一下tcp沾包拆包问题发生的过程:

  1. 当socket连接建立后,内核会为每个socket通道新建一个发送缓冲区和接收缓冲区.
  2. 发送数据时候,应用程序将数据写入发送缓冲区,这里有三种情况:
    • 数据包太长:单个tcp报文无法将数据发送完,这里则需要进行分包,将一个应用层报文分成两个或多个包发送。实际上这里通常有三个原因:
      • write写入数据大于发送缓冲区大小
      • 缓冲区足够大,但是MSS/MTU的限制不得不进行拆包;
      • 滑动窗口大小的限制;
    • 数据包太短:当数据报文内容比较小的时候,发送缓冲区是采取的限制时间内攒一波发送的策略。此时就涉及到了同一个数据包中可能包含多个数据包的情况。
  3. 接收数据时,应用程序从接收缓冲区取数据,由于发送的数据存在沾包的问题,因此接收端应用程序,必须将缓冲区进行必要的处理,进行识别,否则就会出现应用报文解析错误的问题!

1.2 沾包的四种现象

  1. 服务端分别收取了两个数据包,没有沾包和拆包;
  2. 服务端一次收到了两个数据包,数据包D1和D2 沾合到一起了;被称为tcp沾包
  3. 服务到两次读取到了两个数据包,第一次读取到了完成的D1包和D2包的部分内容,第二次读取到了D2包的部分内容;被称为tcp拆包
  4. 幅度段端两次读取到了两个数据包,第一次读取到了D1的部分内容,第二次读取到了D1的剩余内容和D2完整内容。被称为tcp拆包

1.3 沾包拆包问题的本质

沾包拆包问题的本质由于tcp协议是一个基于流的协议,并没有界定上层应用层数据的界限!

一个完整的应用层数据包,可能会被tcp拆分成多个包发送,也可能是多个小数据包组装成一个大的数据包发送,这便是所谓的tcp沾包拆包问题。

1.4 tcp沾包拆包问题解决思路

常见的沾包拆包问题解决思路可以总结为两种情况:

  1. 将应用数据报文定长–固定数据包长度,应用层则可以很简单的进行数据内容的识别
  2. 变长情况:该情况稍微复杂,通常有两种做法:
    1. 为应用层报文添加指定的的分隔符,比如\n\r ]]}]]
    2. 使用tlv结构,在应用层报文头部,明确消息类型和具体的长度!

本文讲解的odl的三个南向协议都涉及到了这个解决思路!

2 netty的沾包拆包之道

在解决tcp沾包拆包导致的半包读写问题上,netty提供了多种编码器用于解决半包问题,现将相关编码器总结如下:

  1. LineBaseFrameDecoder:LineBaseFrameDecoder的工作原理是依次遍历ByteBuf的可读字节,如果有\n或者\n\r,就以此位置为结束。
  2. StringDecoder:StringDecoder的功能是将接受对象转换成字符串,继续调用后续的handler;
  3. DelimiterBasedFrameDecoder:DelimiterBasedFrameDecoder是一种分隔符加码器,可以自动完成对指定分隔符结束的消息的解码;
  4. FixedLengthFrameDecoder:FixedLengthFrameDecoder可以自动完成对于定长消息的解码;

根据上面分析可知,netty框架对于简单的终止符分割、定长消息解码给出了自己的解码器,方便用户进行解码,但是对于复杂的tlv结构没有很好办法,但是给出了相应接口,支持用户自行的扩展,本文则是以此为基础重点分析以下opendaylight的三个重要协议如何解决这个问题!

2 netconf 协议沾包粘包问题解决

下图所示是netconf协议子通道初始化的位置,这里有两个比较有引人注意的handler添加到了pipline。

在这里插入图片描述

首先我们看看NetconfEOMAggregator,可以看到它便是我们寻找的终止符解码器,继承了DelimiterBasedFrameDecoder,然后为其传入了一个netconf协议规定的终止符]]>]]>

需要明确的是DelimiterBasedFrameDecoder是一个入方向接收解码器

在这里插入图片描述

其次我们来看看报文的出方向FramingMechanismHandlerFactory,工厂传入的参数为EOM,这是由netconf协议相关的,参考rfc6242#section-4.3,在协议框架上采用了终止符机制,因此每次发送数据也有必要通过一个编码器加上netconf固定的后缀]]>]]>,这便是EOMFramingMechanismEncoder的工作,由继承关系可知,它是一个输出方向的编码器–ChannelOutboundHandlerAdapter。

在这里插入图片描述

3 openflow协议的沾包粘包问题解决

openflow协议是一个tlv结构的报文,其具有非常明确的报文协议头部,包含了消息类型、长度以及子tlv结构等内容,详细细节可查看of组织定义的标准openflow协议rfc文档说明!

为了找到openflow协议channel初始化的地方,我们先聚焦到tcpHandler的初始化位置,这里可以找到我们所关注的tcp通道初始化器
在这里插入图片描述

如下图所示为TcpChannelInitializer的初始化通道代码,由于openflow消息可能会被tls协议加密,所以最初加入的handler是一个tls的hanler,专用于tls协议的解析!

在这里插入图片描述

下图是所示,完成tls解析后加入的第一个handler,便是openflow消息框架的handler!

我们期待的沾包拆包功能便是由openflow自己重写的这个OFFrameDecoder完成!

在这里插入图片描述

这个解码器的功能就是识别出openflow的报文头部,进行消息的完整性检查,具体逻辑如下:

  • 取出openflow报文头部,如果读取字节小于报文头长度则返回;
  • 取把报文长度位,若可读字节长度低于报文长度,则返回,暂时不进行后续处理!
  • 若长度够,则按长度取出数据,并偏移相关ByteBuf
    在这里插入图片描述

由上文图文分析可知,openflow的沾包处理是自己定义了一个OFFrameDecoder框架解码器,通过tlv报文长度入手,缓冲区达到长度则处理,加入对象列表,否则直接返回,等待下一次触发处理!

4 ovsdb协议的沾包粘包问题解决

ovsdb的协议采用json rpc格式进行协议消息传递,具体协议细节,有兴趣的读者同样可以参看rfc文档,查看相应细节!

按照openflow协议的细节,我们很快找到相应的协议处理类,便是这里的JsonRpcDecoder

StringEncoder,一个负责解码,一个负责编码!

在这里插入图片描述

json rpc消息的框架解码器,在这里做的工作便是识别一个完整的json消息,如下图方框便是做的这个工作,但是存在有一种可能,就是可能一个handler的处理的时候不能形成json的那种开合处理!也就是我们所说的半包,ovsdb模块的处理逻辑是使用了一个临时变量lastRecordBytes,假如每次处理的是一个整包,这个值会归零,但是一旦处理不完全,变不会想相关消息内容加入list,并且会记录住这里的lastRecordBytes,下一次处理的时候把它在加入缓冲字节一并处理!

在这里插入图片描述

在这里插入图片描述

5 小结说明

本节分析了tcp粘包拆包问题的具体原因和本质,同时针对netty并结合opendaylight这个开源网络控制器,针对其开发的三个rfc标准协议openflow 、netconf以及ovsdb给出了相关沾包拆包的实现细节!可以看到随着rfc协议的复杂化,沾包拆包问题,更倾向于使用特定分隔符以及用户自定义tlv编码解码器完成相关处理!

这里给出的三个协议的实现,更是一种参考!值得记录下来回味一番,确实netty在tcp沾包拆包的处理上面提供了很大的灵活度和扩展性,这种设计思路值得借鉴!可能有些读者对于netty的handler的读写回调还有些问题,本文之后下一节将会专门给出说明!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值