zoukankan      html  css  js  c++  java
  • TCP粘包处理RingBuf方法

    TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。粘包可能由发送方造成,也可能由接收方造成。TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据,造成多个数据包的粘连。如果接收进程不及时接收数据,已收到的数据就放在系统接收缓冲区,用户进程读取数据时就可能同时读到多个数据包。因为系统传输的数据是带结构的数据,需要做分包处理。

    为了适应高速复杂网络条件,我们设计实现了粘包处理模块,由接收方通过预处理过程,对接收到的数据包进行预处理,将粘连的包分开。为了方便粘包处理,提高处理效率,在接收环节使用了环形缓冲区来存储接收到的数据。其结构如表1所示。

                                                                1 环形缓冲结构

    字段名

    类型

    含义

    CS

    CRITICAL_SECTION

    保护环形缓冲的临界区

    pRingBuf

    UINT8*

    缓冲区起始位置

    pRead

    UINT8*

    当前未处理数据的起始位置

    pWrite

    UINT8*

    当前未处理数据的结束位置

    pLastWrite

    UINT8*

    当前缓冲区的结束位置

    环形缓冲跟每个TCP套接字绑定。在每个TCP的SOCKET_OBJ创建时,同时创建一个PRINGBUFFER结构并初始化。这时候,pRingBuf指向环形缓冲区的内存首地址,pRead、pWrite指针也指向它。pLastWrite指针在这时候没有实际意义。初始化之后的结构如图1所示。

    1 初始化后的环形缓冲区

    在每次投递一个TCP的接收操作时,从RINGBUFFER获取内存作接收缓冲区,一般规定一个最大值L1作为可以写入的最大数据量。这时把pWrite的值赋给BUFFER_OBJ的buf字段,把L1赋给bufLen字段。这样每次接收到的数据就从pWrite开始写入缓冲区,最多写入L1字节,如图 2。

    图2 分配缓冲后的环形缓冲

    如果某次分配过程中,pWrite到缓冲区结束的位置pEnd长度不够最小分配长度L1,为了提高接收效率,直接废弃最后一段内存,标记pLastWrite为pWrite。然后从pRingBuf开始分配内存,如图 3。

    3 使用到结尾的环形缓冲

    特殊情况下,如果处理包速度太慢,或者接收太快,可能导致未处理包占用大部分缓冲区,没有足够的缓冲区分配给新的接收操作,如图4。这时候直接报告错误即可。

    4没有足够接收缓冲的环形缓冲

    当收到一个长度为L数据包时,需要修改缓冲区的指针。这时候已经写入数据的位置变为(pWrite+L),如图 5。

    5收到长度为L的数据的环形缓冲

    分析上述环形缓冲的使用过程,收到数据后的情况可以简单归纳为两种:pWrite>pRead,接收但未处理的数据位于pRead到pWrite之间的缓冲区;pWrite<pRead,这时候,数据位于pRead到pLastWrite和pRingbuf到pWrite之间。这两种情况分别对应图 6、图 7。

    首先分析图6。此时,pRead是一个包的起始位置,如果L1足够一个包头长度,就获取该包的长度信息,记为L。假如L1>L,就说明一个数据包接收完成,根据包类型处理包,然后修改pRead指针,指向下一个包的起始位置(pRead+L)。这时候仍然类似于之前的状态,于是解包继续,直到L1不足一个包的长度,或者不足包头长度。这时退出解包过程,等待后续的数据到来。

    6有未处理数据的环形缓冲(1

    7有未处理数据的环形缓冲(2

    图 8稍微复杂。首先按照上述过程处理L1部分。存在一种情况,经过若干个包处理之后,L1不足一个包,或者不足一个包头。如果这时(L1+L2)足够一个包的长度,就需要继续处理。另外申请一个最大包长度的内存区pTemp,把L1部分和L2的一部分复制到pTemp,然后执行解包过程。

    经过上述解包之后,pRead就转向pRingBuf到pWrite之间的某个位置,从而回归情况图 6,继续按照图 6部分执行解包。

  • 相关阅读:
    start tag, end tag issues in IE7, particularly in xslt transformation
    用SandCastle为注释生成chm文档
    Firebug
    架构的重点
    Linux Shell常用技巧(十) 管道组合
    Linux JDK升级
    Linux Shell常用技巧(十二) Shell编程
    Packet Tracer 5.0实验(一) 交换机的基本配置与管理
    Linux Shell常用技巧(六) sort uniq tar split
    Linux Shell常用技巧(二) grep
  • 原文地址:https://www.cnblogs.com/sunwei2012/p/1798011.html
Copyright © 2011-2022 走看看