310 lines
9.9 KiB
C#
310 lines
9.9 KiB
C#
using System;
|
|
|
|
namespace UdtSharp
|
|
{
|
|
public class ACKWindow
|
|
{
|
|
public ACKWindow(int size = 1024)
|
|
{
|
|
m_iSize = size;
|
|
m_piACKSeqNo = new int[m_iSize];
|
|
m_piACK = new int[m_iSize];
|
|
m_pTimeStamp = new ulong[m_iSize];
|
|
|
|
m_piACKSeqNo[0] = -1;
|
|
}
|
|
|
|
// Functionality:
|
|
// Write an ACK record into the window.
|
|
// Parameters:
|
|
// 0) [in] seq: ACK seq. no.
|
|
// 1) [in] ack: DATA ACK no.
|
|
// Returned value:
|
|
// None.
|
|
|
|
public void store(int seq, int ack)
|
|
{
|
|
m_piACKSeqNo[m_iHead] = seq;
|
|
m_piACK[m_iHead] = ack;
|
|
m_pTimeStamp[m_iHead] = Timer.getTime();
|
|
|
|
m_iHead = (m_iHead + 1) % m_iSize;
|
|
|
|
// overwrite the oldest ACK since it is not likely to be acknowledged
|
|
if (m_iHead == m_iTail)
|
|
m_iTail = (m_iTail + 1) % m_iSize;
|
|
}
|
|
|
|
// Functionality:
|
|
// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT .
|
|
// Parameters:
|
|
// 0) [in] seq: ACK-2 seq. no.
|
|
// 1) [out] ack: the DATA ACK no. that matches the ACK-2 no.
|
|
// Returned value:
|
|
// RTT.
|
|
|
|
public int acknowledge(int seq, ref int ack)
|
|
{
|
|
if (m_iHead >= m_iTail)
|
|
{
|
|
// Head has not exceeded the physical boundary of the window
|
|
|
|
for (int i = m_iTail, n = m_iHead; i < n; ++i)
|
|
{
|
|
// looking for indentical ACK Seq. No.
|
|
if (seq == m_piACKSeqNo[i])
|
|
{
|
|
// return the Data ACK it carried
|
|
ack = m_piACK[i];
|
|
|
|
// calculate RTT
|
|
int rtt = (int)(Timer.getTime() - m_pTimeStamp[i]);
|
|
|
|
if (i + 1 == m_iHead)
|
|
{
|
|
m_iTail = m_iHead = 0;
|
|
m_piACKSeqNo[0] = -1;
|
|
}
|
|
else
|
|
m_iTail = (i + 1) % m_iSize;
|
|
|
|
return rtt;
|
|
}
|
|
}
|
|
|
|
// Bad input, the ACK node has been overwritten
|
|
return -1;
|
|
}
|
|
|
|
// Head has exceeded the physical window boundary, so it is behind tail
|
|
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)(Timer.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;
|
|
}
|
|
|
|
int[] m_piACKSeqNo; // Seq. No. for the ACK packet
|
|
int[] m_piACK; // Data Seq. No. carried by the ACK packet
|
|
ulong[] m_pTimeStamp; // The timestamp when the ACK was sent
|
|
|
|
int m_iSize; // Size of the ACK history window
|
|
int m_iHead; // Pointer to the lastest ACK record
|
|
int m_iTail; // Pointer to the oldest ACK record
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public class PktTimeWindow
|
|
{
|
|
public PktTimeWindow(int asize = 16, int psize = 16)
|
|
{
|
|
m_iAWSize = asize;
|
|
m_iPWSize = psize;
|
|
m_iMinPktSndInt = 1000000;
|
|
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 = Timer.getTime();
|
|
|
|
for (int i = 0; i < m_iAWSize; ++i)
|
|
m_piPktWindow[i] = 1000000;
|
|
|
|
for (int k = 0; k < m_iPWSize; ++k)
|
|
m_piProbeWindow[k] = 1000;
|
|
}
|
|
|
|
// Functionality:
|
|
// read the minimum packet sending interval.
|
|
// Parameters:
|
|
// None.
|
|
// Returned value:
|
|
// minimum packet sending interval (microseconds).
|
|
|
|
public int getMinPktSndInt()
|
|
{
|
|
return m_iMinPktSndInt;
|
|
}
|
|
|
|
// Functionality:
|
|
// Calculate the packes arrival speed.
|
|
// Parameters:
|
|
// None.
|
|
// Returned value:
|
|
// Packet arrival speed (packets per second).
|
|
|
|
public int getPktRcvSpeed()
|
|
{
|
|
// get median value, but cannot change the original value order in the window
|
|
Array.Copy(m_piPktWindow, m_piPktReplica, m_iAWSize - 1); // why -1 ???
|
|
Array.Sort(m_piPktReplica); // need -1 here ???
|
|
int median = m_piPktReplica[m_iAWSize / 2];
|
|
|
|
int count = 0;
|
|
int sumMicrosecond = 0;
|
|
int upper = median << 3;
|
|
int lower = median >> 3;
|
|
|
|
// median filtering
|
|
for (int i = 0, n = m_iAWSize; i < n; ++i)
|
|
{
|
|
if ((m_piPktWindow[i] < upper) && (m_piPktWindow[i] > lower))
|
|
{
|
|
++count;
|
|
sumMicrosecond += m_piPktWindow[i];
|
|
}
|
|
}
|
|
double packetsPerMicrosecond = (double)count / sumMicrosecond;
|
|
|
|
// claculate speed, or return 0 if not enough valid value
|
|
if (count > (m_iAWSize >> 1))
|
|
return (int)Math.Ceiling(1000000 * packetsPerMicrosecond);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Functionality:
|
|
// Estimate the bandwidth.
|
|
// Parameters:
|
|
// None.
|
|
// Returned value:
|
|
// Estimated bandwidth (packets per second).
|
|
|
|
public int getBandwidth()
|
|
{
|
|
// get median value, but cannot change the original value order in the window
|
|
Array.Copy(m_piProbeWindow, m_piProbeReplica, m_iPWSize - 1); // why -1 ???
|
|
Array.Sort(m_piProbeReplica); // need -1 here ???
|
|
int median = m_piProbeReplica[m_iPWSize / 2];
|
|
|
|
int count = 1;
|
|
int sum = median;
|
|
int upper = median << 3;
|
|
int lower = median >> 3;
|
|
|
|
// median filtering
|
|
for (int i = 0, n = m_iPWSize; i < n; ++i)
|
|
{
|
|
if ((m_piProbeWindow[i] < upper) && (m_piProbeWindow[i] > lower))
|
|
{
|
|
++count;
|
|
sum += m_piProbeWindow[i];
|
|
}
|
|
}
|
|
|
|
return (int)Math.Ceiling(1000000.0 / ((double)sum / (double)count));
|
|
}
|
|
|
|
// Functionality:
|
|
// Record time information of a packet sending.
|
|
// Parameters:
|
|
// 0) currtime: timestamp of the packet sending.
|
|
// Returned value:
|
|
// None.
|
|
|
|
public void onPktSent(int currtime)
|
|
{
|
|
int interval = currtime - m_iLastSentTime;
|
|
|
|
if ((interval < m_iMinPktSndInt) && (interval > 0))
|
|
m_iMinPktSndInt = interval;
|
|
|
|
m_iLastSentTime = currtime;
|
|
}
|
|
|
|
// Functionality:
|
|
// Record time information of an arrived packet.
|
|
// Parameters:
|
|
// None.
|
|
// Returned value:
|
|
// None.
|
|
|
|
public void onPktArrival()
|
|
{
|
|
m_CurrArrTime = Timer.getTime();
|
|
|
|
// record the packet interval between the current and the last one
|
|
m_piPktWindow[m_iPktWindowPtr] = (int)(m_CurrArrTime - m_LastArrTime);
|
|
|
|
// the window is logically circular
|
|
++m_iPktWindowPtr;
|
|
if (m_iPktWindowPtr == m_iAWSize)
|
|
m_iPktWindowPtr = 0;
|
|
|
|
// remember last packet arrival time
|
|
m_LastArrTime = m_CurrArrTime;
|
|
}
|
|
|
|
// Functionality:
|
|
// Record the arrival time of the first probing packet.
|
|
// Parameters:
|
|
// None.
|
|
// Returned value:
|
|
// None.
|
|
|
|
public void probe1Arrival()
|
|
{
|
|
m_ProbeTime = Timer.getTime();
|
|
}
|
|
|
|
// Functionality:
|
|
// Record the arrival time of the second probing packet and the interval between packet pairs.
|
|
// Parameters:
|
|
// None.
|
|
// Returned value:
|
|
// None.
|
|
|
|
public void probe2Arrival()
|
|
{
|
|
m_CurrArrTime = Timer.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;
|
|
}
|
|
|
|
int m_iAWSize; // size of the packet arrival history window
|
|
int[] m_piPktWindow; // packet information window
|
|
int[] m_piPktReplica;
|
|
int m_iPktWindowPtr; // position pointer of the packet info. window.
|
|
|
|
int m_iPWSize; // size of probe history window size
|
|
int[] m_piProbeWindow; // record inter-packet time for probing packet pairs
|
|
int[] m_piProbeReplica;
|
|
int m_iProbeWindowPtr; // position pointer to the probing window
|
|
|
|
int m_iLastSentTime; // last packet sending time
|
|
int m_iMinPktSndInt; // Minimum packet sending interval
|
|
|
|
ulong m_LastArrTime; // last packet arrival time
|
|
ulong m_CurrArrTime; // current packet arrival time
|
|
ulong m_ProbeTime; // arrival time of the first probing packet
|
|
}
|
|
}
|