zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    【纯水题】POJ 1852 Ants
    【树形DP】BZOJ 1131 Sta
    【不知道怎么分类】HDU
    【树形DP】CF 1293E Xenon's Attack on the Gangs
    【贪心算法】CF Emergency Evacuation
    【思维】UVA 11300 Spreading the Wealth
    【树形DP】NOI2003 逃学的小孩
    【树形DP】BZOJ 3829 Farmcraft
    【树形DP】JSOI BZOJ4472 salesman
    【迷宫问题】CodeForces 1292A A NEKO's Maze Game
  • 原文地址:https://www.cnblogs.com/ukernel/p/9191069.html
Copyright © 2011-2022 走看看