• UDT源码剖析(十二)之ACKWindow


    通过窗口记录发送时间以及ACK接收情况。

    CACKWindow

    class CACKWindow
    {
    private:
       //是三个独立的数组 
       int32_t* m_piACKSeqNo;       // 记录ACK序列号
       int32_t* m_piACK;            // 记录数据序列号
       uint64_t* m_pTimeStamp;      //记录ACK的发送时间
    
       int m_iSize;                 // ACK窗口的大小    
       int m_iHead;                 // 记录最后一次ACK,同步的维护三个数组的顺序
       int m_iTail;                 // 记录存在时间最长的ACK
    };
    
    • 初始化:CACKWindow::CACKWindow(int size)
    CACKWindow::CACKWindow(int size):
    m_piACKSeqNo(NULL),
    m_piACK(NULL),
    m_pTimeStamp(NULL),
    m_iSize(size),
    m_iHead(0),
    m_iTail(0)
    {
       m_piACKSeqNo = new int32_t[m_iSize];    //分别初始化三个记录窗口,并设置首个ACK记录窗口
       m_piACK = new int32_t[m_iSize];    //不用看了,size = 1024
       m_pTimeStamp = new uint64_t[m_iSize];
    
       m_piACKSeqNo[0] = -1;
    }
    
    • 更新存储的序列号:void CACKWindow::store(int32_t seq, int32_t ack)
    void CACKWindow::store(int32_t seq, int32_t ack)
    {
       m_piACKSeqNo[m_iHead] = seq;    //记录ACK序列号
       m_piACK[m_iHead] = ack;    //记录数据序列号
       m_pTimeStamp[m_iHead] = CTimer::getTime();    //记录获取时间
    
       m_iHead = (m_iHead + 1) % m_iSize;   //写完后调整位置
    
       if (m_iHead == m_iTail)  //如果oldest ACK没有被确认的话
          m_iTail = (m_iTail + 1) % m_iSize;    //将oldest ACK往后调整
    }
    
    int CACKWindow::acknowledge(int32_t seq, int32_t& ack)    //当收到一个packet时,从记录中取出发送时的ACK以及发送时的事件记录,并计算RTT
    {
       if (m_iHead >= m_iTail)  //头部没有超过窗口的物理边界
       {
          for (int i = m_iTail, n = m_iHead; i < n; ++ i)
          {
             // looking for indentical ACK Seq. No.
             if (seq == m_piACKSeqNo[i])    //寻找ACK seq 
             {
                // return the Data ACK it carried
                ack = m_piACK[i];   //记录ACK
    
                // calculate RTT
                int rtt = int(CTimer::getTime() - m_pTimeStamp[i]); //计算RTT
    
                if (i + 1 == m_iHead)   //调整Tail的位置
                {
                   m_iTail = m_iHead = 0;   //为每个连接保存三个数字就可以计算RTT,为什么要通过这种方式?难道每一次发送都需要计算RTT吗?不是吧,应该是每一个间隔计算一次
                   m_piACKSeqNo[0] = -1;
                }
                else
                   m_iTail = (i + 1) % m_iSize;
    
                return rtt; //返回计算的RTT
             }
          }
    
          // Bad input, the ACK node has been overwritten
          return -1;
       }
    
       //出现这种情况的情况是,延迟比较大,出现了回绕的情况,不过作用不变,还是计算RTT并更新确认数据的位置
       for (int j = m_iTail, n = m_iHead + m_iSize; j < n; ++ j)
       {
          // looking for indentical ACK seq. no.
          if (seq == m_piACKSeqNo[j % m_iSize])
          {
             // return Data ACK
             j %= m_iSize;
             ack = m_piACK[j];
    
             // calculate RTT
             int rtt = int(CTimer::getTime() - m_pTimeStamp[j]);
    
             if (j == m_iHead)
             {
                m_iTail = m_iHead = 0;
                m_piACKSeqNo[0] = -1;
             }
             else
                m_iTail = (j + 1) % m_iSize;
    
             return rtt;
          }
       }
    
       // bad input, the ACK node has been overwritten
       return -1;
    }
    

    CPktTimeWindow

    class CPktTimeWindow    //记录数据包的时间窗口
    {
    private:
       int m_iAWSize;                   // 分组到达时历史窗口的大小
       int* m_piPktWindow;          // 分组信息窗口
       int* m_piPktReplica;
       int m_iPktWindowPtr;         // 分组信息窗口的位置指针
    
       int m_iPWSize;               // 探测历史窗口打下
       int* m_piProbeWindow;        // 记录用于探测分组的分组间时间
       int* m_piProbeReplica;
       int m_iProbeWindowPtr;       // 位置指针指向探测窗口
    
       int m_iLastSentTime;         // 最后一个packet的发送时间
       int m_iMinPktSndInt;         // 最小的包发送时间间隔
    
       uint64_t m_LastArrTime;      // 最后一个包的到达时间
       uint64_t m_CurrArrTime;      // 当前包的到达时间
       uint64_t m_ProbeTime;        // 第一个探测分组的到达时间
    };
    
    • 初始化:CPktTimeWindow::CPktTimeWindow(int asize, int psize)
    CPktTimeWindow::CPktTimeWindow(int asize, int psize):
    m_iAWSize(asize),
    m_piPktWindow(NULL),
    m_iPktWindowPtr(0),
    m_iPWSize(psize),
    m_piProbeWindow(NULL),
    m_iProbeWindowPtr(0),
    m_iLastSentTime(0),
    m_iMinPktSndInt(1000000),
    m_LastArrTime(),
    m_CurrArrTime(),
    m_ProbeTime()
    {
       m_piPktWindow = new int[m_iAWSize];    //分组到达时间
       m_piPktReplica = new int[m_iAWSize];    //临时窗口,计算的时候会用到
       m_piProbeWindow = new int[m_iPWSize];    //记录探测分组的分组间时间
       m_piProbeReplica = new int[m_iPWSize];    //临时窗口,计算的时候会用到
    
       m_LastArrTime = CTimer::getTime();
    
       for (int i = 0; i < m_iAWSize; ++ i)
          m_piPktWindow[i] = 1000000;   //分组到达时间全部设置为1S的吗??
    
       for (int k = 0; k < m_iPWSize; ++ k)
          m_piProbeWindow[k] = 1000;    //探测分组的时间间隔都设置为1ms的吗??
    }
    
    • 获取最小的包发送时间间隔:int CPktTimeWindow::getMinPktSndInt() const
    int CPktTimeWindow::getMinPktSndInt() const
    {
       return m_iMinPktSndInt;  //初始化的时候是1S,不会吧...后面应该会更新
    }
    
    • 计算分组到达速率:int CPktTimeWindow::getPktRcvSpeed() const
    int CPktTimeWindow::getPktRcvSpeed() const    //计算分组到达速率(packets per second)
    {
       // get median value, but cannot change the original value order in the window
       std::copy(m_piPktWindow, m_piPktWindow + m_iAWSize - 1, m_piPktReplica);
       //从第n个元素开始,使第n大的元素位于第n个位置,比这个元素小的元素都排在这个元素之后,不过不保证有序
       std::nth_element(m_piPktReplica, m_piPktReplica + (m_iAWSize / 2), m_piPktReplica + m_iAWSize - 1);
       int median = m_piPktReplica[m_iAWSize / 2];  //获得窗口大小的中位数
    
       int count = 0;
       int sum = 0;
       int upper = median << 3; //窗口大小*8
       int lower = median >> 3; //窗口大小/8
    
       // median filtering(中值滤波)
       int* p = m_piPktWindow;  //从头开始遍历一次
       for (int i = 0, n = m_iAWSize; i < n; ++ i)
       {
          if ((*p < upper) && (*p > lower)) //使用这种方法过滤一遍有用吗??
          {
             ++ count;
             sum += *p;
          }
          ++ p;
       }
    
       // claculate speed, or return 0 if not enough valid value
       if (count > (m_iAWSize >> 1))    //如果count大于测量的一半,就是有效的计算
          return (int)ceil(1000000.0 / (sum / count));  
       else
          return 0;
    }
    
    • 计算目前的带宽:int CPktTimeWindow::getBandwidth() const
    int CPktTimeWindow::getBandwidth() const    //使用中值滤波算法,通过观察窗口的大小,计算目前的带宽
    {
       // get median value, but cannot change the original value order in the window
       std::copy(m_piProbeWindow, m_piProbeWindow + m_iPWSize - 1, m_piProbeReplica);
       std::nth_element(m_piProbeReplica, m_piProbeReplica + (m_iPWSize / 2), m_piProbeReplica + m_iPWSize - 1);
       int median = m_piProbeReplica[m_iPWSize / 2];
    
       int count = 1;
       int sum = median;
       int upper = median << 3; //起始就是去掉波动很大的数值
       int lower = median >> 3;
    
       // median filtering
       int* p = m_piProbeWindow;
       for (int i = 0, n = m_iPWSize; i < n; ++ i)
       {
          if ((*p < upper) && (*p > lower))
          {
             ++ count;
             sum += *p;
          }
          ++ p;
       }
    
       return (int)ceil(1000000.0 / (double(sum) / double(count)));
    }
    
    • 更新发送时间,发送时调用:void CPktTimeWindow::onPktSent(int currtime)
    void CPktTimeWindow::onPktSent(int currtime)    //回调函数,发送时的调用
    {
       int interval = currtime - m_iLastSentTime;   //上一次发送与本次发送的时间间隔
    
       if ((interval < m_iMinPktSndInt) && (interval > 0))//更新发送的时间间隔,果然是动态更新的,RTT
          m_iMinPktSndInt = interval;
    
       m_iLastSentTime = currtime;
    }
    
    • 更新接收时间,接收时调用:void CPktTimeWindow::onPktArrival()
    void CPktTimeWindow::onPktArrival()    //回调函数,接受时调用
    {
       m_CurrArrTime = CTimer::getTime();    //获取当前时间
    
       *(m_piPktWindow + m_iPktWindowPtr) = int(m_CurrArrTime - m_LastArrTime); //在数组中记录接受事件的间隔,用于计算RTT
    
       // the window is logically circular
       ++ m_iPktWindowPtr;  //更改窗口位置指针
       if (m_iPktWindowPtr == m_iAWSize)
          m_iPktWindowPtr = 0;
    
       // remember last packet arrival time
       m_LastArrTime = m_CurrArrTime;   //记录数据到达时间
    }
    
    • 记录第一个探测分组到达的时间:void CPktTimeWindow::probe1Arrival()
    void CPktTimeWindow::probe1Arrival()
    {
       m_ProbeTime = CTimer::getTime(); //记录第一个探测分组到达的时间
    }
    
    • 记录第二个探测分组到达的时间以及数据包之间的间隔:void CPktTimeWindow::probe2Arrival()
    void CPktTimeWindow::probe2Arrival()    //记录第二个探测分组到达的时间以及数据包之间的间隔
    {
       m_CurrArrTime = CTimer::getTime();
    
       // record the probing packets interval
       *(m_piProbeWindow + m_iProbeWindowPtr) = int(m_CurrArrTime - m_ProbeTime);   //在数组中几率
       // the window is logically circular
       ++ m_iProbeWindowPtr;
       if (m_iProbeWindowPtr == m_iPWSize)
          m_iProbeWindowPtr = 0;
    }
    
  • 相关阅读:
    mysql重置id
    mysql数据类型
    手把手教你新建一个Vue项目
    用markdown开始优雅的写作
    源码阅读心得
    断点调试-程序员的必修课
    代码还是短点好!
    GoJS v1.8.27 去水印方法
    VS code不用集成终端如何修改并推送分支?
    LeetCode日拱一卒
  • 原文地址:https://www.cnblogs.com/ukernel/p/9191069.html
Copyright © 2020-2023  润新知