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;
}
```

猜你喜欢

转载自www.cnblogs.com/ukernel/p/9191069.html
今日推荐