UDP可靠传输:UDX协议源码发布

算法实现语言:C++

平台:win/linux

演示程序:https://webboy.lanzoul.com/i68wg5c 密码:udx

1,带宽。

对带宽的评估,预测。

l         在检测最大发送窗口的时候,是参照RENO算法,丢包检测.但是在之个过程中,UDX还检测了ACK的回复率,当出现ACK回复频率发生变化(变化率K > 0.35)时表明现在网络出现了波动,可以预测已经达到拥塞临界,这有点象VEGAS一样,可以提前预测出现拥塞,这时UDX调整慢启动阀值,提前进入拥塞避免阶段.

2,快速重传

UDX的ACK设计,其实就是SACK协议,但是实现手法是不同于SACK,sack是首尾序号对,而UDX是一个起始序号,及后面的相对序号组成,这样可以容纳更多的ACK.当发送方收到任何一个ACK时就可以准确的确定哪个包已经丢掉了,这时可以马上重传,而不需要等到超时后再重传,从而提高了实时性及间接的提高了吞吐量。

3,AIDM上有何改进

慢启动阶段和TCP类是W += 1,拥塞避免阶段是W += 1/W;

UDX在窗口管理上,也采用了不同的设计方法。其中引入了一个饱和状态,也就是最佳状态。而实际发送速度是这个最佳状态时的1.25倍发送速度。这样可以保证较高的竟争性。

UDX中,度量的单位是当前流量,控制窗口的核心思想是,控制流量的增量。当增量趋近于实际流量的<= 5%波动时,认为,流量已经最大,这时进入饱和状态,当UDX进入饱和状态时,转入拥塞避免加速模式,窗口不增大反而会减小,达到一个动态平横,使发送速度,始终稳定在一个水平上。

4,算法上有何优化

主要优化在于重传策略,及超时时间计算

重传策略上不同与其他算法,主要是

超时重传是由ACK驱动,而不是仅依靠定时器,这样可以更快的重传数据,增加实时性。

由于UDP的特性,目前 UDX是采用1.5倍计算的超时时间。超时时间为RTT + 4*|dRTT|

如果按传统的TCP的指数退避算法计算超时时间,在丢包和大延迟的网络,性能会急剧下降。

UDX主要重传发生在ACK到来时,由于收到ACK,说明网络正常,这时重发数据,比盲目依靠定时器重传可靠的多。UDX定时器只会每次发一个重发包,当网络恢复时,就会重新依靠ACK把重发包快速发出去。

5,针对wifi调整了什么参数

在无线3G领域,UDX没有做过多的优化,无线网络主要特性和有线网络还是比较明显。其特点是,波动大,流量不稳定,丢包率不固定,完全受环境的信号影响。

从这个特性上来看,UDX的若干改进还是比较适合这种环境,较其他算法,更有抗丢包和干扰。比如,ACK设计及快速重传等。

6,在多媒体传输上的优势

Udx主要体现在其实时性,特别适合传送视频,网络越复杂,环境 越是恶劣,越是适合UDX。对于音频,UDX适合在低延迟,低丢包率的网络(RTT小于150MS,丢包小于5%)。因为是可靠数据传播 ,相对于RTP这类实时协议音质要好很多。但实时性略差,或人感觉不出来,在丢包环境,只要延迟小于100ms,语音较RTP有较大提高,实时性也感觉不出来。

7.UDX接口。

提供了流式接口,包接口。

处理粘包问题,使用户只处理业务包,可以发送较大的业务包。接收方,收到的也只是业务包。

提拱P2P,接口及中转接口

8.参数设置

1.UDX全局设置

包括最小传输单元MSS

内部时钟

最小ACK回复间隔

最小超时

最大超时

多少个包应答一次

及最小初使窗口

2.UDX单个连接设置

连接超时

心跳包间隔

是否属于固定流量发送

流量估算由包个数转变为字节,使控制更加精确。

支持碎包。

支持多IP,在多网卡上转发,效率提高。

联接断开:
UDX采用是瘦断开,或叫不安全断开,向远端发送四次断开包,本地立即断开。

见以下附部分源码:

协议帧格式:

#ifdef WIN32
#pragma pack( push, 1 )
#define PACKED
#else
#define PACKED __attribute__((packed, aligned(1)))
#endif

struct _UdpHead //7
{
UDP_SHORT sendtime;
UDP_LONG SegIndex;
UDP_BYTE brto:1;
UDP_BYTE bnosacks:1;
UDP_BYTE type:4;
UDP_BYTE cid:2;
}PACKED;

struct UdpHead //10 BYTE
{
UDP_BYTE bP2p:1;
UDP_BYTE bMquery:1;
UDP_BYTE bMregister:1;
UDP_BYTE bMTrans:1;
UDP_BYTE noused:4;
UDP_SHORT sum;//2
_UdpHead head;//7
}PACKED;
struct UdpProxyHead
{
UdpHead head;//10
INT64 des;//8
}PACKED;
struct UdpHeadAck : public UdpHead//ACK 11
{
UDP_BYTE ackcounts;
UDP_BYTE acks[1];
}PACKED;

struct UdxHeadConnect : public UdpHead
{
SOCKADDR wanaddr;
UDP_SHORT maxwndsize;
UDP_SHORT servertime;
UDP_BYTE bAccept : 1;
UDP_BYTE state:7;
UDP_LONG datalen;
UDP_BYTE data[1];
}PACKED;

struct PACKHEAD
{
UDP_SHORT btrans:1;
UDP_SHORT cmd:15;
UDP_LONG translen;
}PACKED;
struct TRANSPACKHEAD : public PACKHEAD
{
UDP_BYTE buffcount;
UDP_SHORT bufflen[1];
}PACKED;

struct FileCmdBase
{
UDP_SHORT a,b,c;
UDP_LONG type;
UDP_LONG cmdlen;
BYTE data[1];
}PACKED;

核心拥塞控制部分代码:部分主要控制变量
double m_uncheckdatasize;//发送出去,还没有被确认的包
double m_sendwinsize;//拥塞窗口
double m_sstresh;//慢启动伐值
double m_lasterWnd;//最大流量时的,窗口
double m_LastMaxFlow;//最大理论流量
double m_deltFlow;//理论流量波动值
double m_lastMaxWnd;//最大发送窗口
double m_AvageFlow;//平均流量
double m_expectwnd;//预期理论窗口
double m_lostrate;

UDP_LONG m_currenttimerbyts;//一个周期内收到的数量
UDP_LONG m_WeekFlowTime;//流量下降时间
UDP_LONG m_cntStatble;//平稳周期计数
UDP_LONG m_LastAckArrivedTime;//最后一次ack到来时间,用来确定重发ACK的周期

int m_cfgwndsize;//约定最大窗口
double m_ackcheckdatatrtt;//控制一个RTT周期增长1.
BOOL m_bQuickRecover;//快速恢复算法

控制算法
// WindowControl.cpp: implementation of the CWindowControl class.
//
//////////////////////////////////////////////////////////////////////

#include “WindowControl.h”
#include “UdxSocket.h”
#include “udxfunction.h”

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWindowControl::CWindowControl()
{
m_pUdx = NULL;
Reset();
}

CWindowControl::~CWindowControl()
{

}

void CWindowControl::OnPackArrived( DWORD datalen )
{
m_LastAckArrivedTime = GetTimer()->GetTickCount();//最后一次,数据到达时间
m_acktimewatcher.OnAck();
m_ackcheckdatatrtt += datalen;

if(datalen == 0)
return;

m_uncheckdatasize -= datalen;
m_currenttimerbyts += datalen;

m_bew.OnAck(datalen);

IncreaseWndSize(TRUE,FALSE,datalen);
}

void CWindowControl::OnPackTimeOutSend( DWORD datalen )
{
if(datalen == 0)
return;
IncreaseWndSize(FALSE,TRUE,datalen);
}

void CWindowControl::OnPackQickReSend( DWORD datalen )
{
if(datalen == 0)
return;
IncreaseWndSize(FALSE,FALSE,datalen);

}

void CWindowControl::OnSendNewData( DWORD datalen )
{
m_uncheckdatasize += datalen;
}

BOOL CWindowControl::IsNetWorkHungry()
{
return m_uncheckdatasize < m_sendwinsize;
}

void CWindowControl::SetCfgWnd( int wnd )
{
m_cfgwndsize = wnd;
}

int CWindowControl::IsOverLoadWnd()
{
assert(m_pUdx);
double currentRtt = m_pUdx->GetRtt()->GetRTT();
double minrtt = m_pUdx->GetRtt()->GetMinTTL();

double act = m_sendwinsize / (currentRtt / 1000);
double expect = m_sendwinsize / (minrtt / 1000);

double DIFF = expect – act;

double a = (0.35f* GetUdxGlobalCfg()->mss) / (minrtt / 1000);
double lastflow = m_lasterWnd / (currentRtt / 1000);
double deltFlow = m_LastMaxFlow – act;
double be = m_bew.GetBew() * 1000;
double be2 = m_reb.GetBew() * 1000;

int deltrtt = 1;
float krtt = currentRtt / __max(10,minrtt);
if(krtt > 2.5f && minrtt > 10)//decrease with rtt
{
deltrtt = __max(krtt*krtt*2,20);
}

double expectwnd = be/*(be + be2) * 0.5f*/ * (__max(UDX_MIN_LOCALNETWORK_RTT,(currentRtt – deltrtt)) / 1000);

if(expectwnd >= GetUdxGlobalCfg()->mss*m_cfgwndsize)
{
expectwnd = GetUdxGlobalCfg()->mss*m_cfgwndsize;
}

BOOL bOverBew = m_bew.IsOverLoad();

m_expectwnd = m_expectwnd*0.95f + expectwnd*0.05f;
m_AvageFlow = 0.85f* m_AvageFlow + 0.15f*act;
m_deltFlow = 0.75f* m_deltFlow + 0.25f*deltFlow;

if(m_LastMaxFlow < act)
{
m_LastMaxFlow = m_LastMaxFlow*0.95f + 0.05f*act;

if(bOverBew)
{
if(currentRtt > minrtt * 1.5f)
m_lasterWnd = m_lasterWnd*0.7f + 0.3f*(__min(m_expectwnd,m_sendwinsize));
else
{
if(currentRtt < minrtt * 3.5f)
m_lasterWnd = m_lasterWnd*0.7f + 0.3f*m_sendwinsize;
}
}
else
{
if(currentRtt < minrtt * 5)
m_lasterWnd = m_lasterWnd*0.7f + 0.3f*m_sendwinsize;
}
if(m_lasterWnd > m_expectwnd * 2)
m_lasterWnd = m_expectwnd * 2;
m_lasterWnd = __max(m_lasterWnd,m_expectwnd / 2);
}

double diff = m_LastMaxFlow * 0.35f;
double maxdiff = __max(a,diff);

if(m_deltFlow > maxdiff)
{
if(GetTimer()->GetTickCount() – m_WeekFlowTime > m_pUdx->GetRtt()->GetTimeOutRto())
{
ATLTRACE(“sss: %0.2f-%0.2f-%0.2f\n”,m_lasterWnd,m_sendwinsize,m_lastMaxWnd);
m_LastMaxFlow *= 0.95f;
m_WeekFlowTime = GetTimer()->GetTickCount();
}
}else
{
m_WeekFlowTime = GetTimer()->GetTickCount();
}

if(DIFF < a)
{
return 0;
}
if(m_acktimewatcher.WaveLargeThan(1) || DIFF > 10*a)//回包波动率
{
if(m_sstresh > 2 * m_sendwinsize)
{
m_sstresh = m_sendwinsize;
}
}
if(m_sendwinsize <= m_expectwnd)
return 0;

float kscan1 = 1.2f;
float kscan2 = 1.15f;

if(minrtt > 10)
{
kscan1 += 0.5f;
kscan2 += 0.5f;
}

float k = m_sendwinsize / m_lasterWnd;//竟争率
if(bOverBew && k > kscan1)
{
ATLTRACE(“TTL OVER LOAD1 %.02f,%.02f,%.02f\n”,k,m_sendwinsize,m_lasterWnd);
return 1;
}
if(m_reb.IsOverLoad() && k > kscan2)
{
ATLTRACE(“TTL OVER LOAD2 %.02f,%.02f,%.02f\n”,k,m_sendwinsize,m_lasterWnd);
return 2;
}
float k2 = (m_sendwinsize * m_lostrate) / m_expectwnd;
if(k2 > kscan1)
{
ATLTRACE(“TTL OVER LOAD3 %.02f,%.02f,%.02f\n”,k,m_sendwinsize,m_lasterWnd);
return 1;
}

if(k2>1 && currentRtt > 2000)
{
ATLTRACE(“TTL OVER LOAD4 %.02f,%.02f,%.02f\n”,k,m_sendwinsize,m_lasterWnd);
return 1;
}

return 0;
}

void CWindowControl::Reset()
{
if(m_pUdx)
m_cfgwndsize = m_pUdx->GetUdxCfg()->wndmaxsize;//约定最大窗口
else
m_cfgwndsize = 0;

m_uncheckdatasize = 0;//发送出去,还没有被确认的包
m_sendwinsize = MIN_SENDWINDOWSIZE;//拥塞窗口
m_sstresh = m_cfgwndsize*GetUdxGlobalCfg()->mss;
m_lasterWnd = MAX_SENDWINDOWSIZE;//最大流量时的,窗口
m_LastMaxFlow = MIN_SENDWINDOWSIZE;//最大理论流量
m_deltFlow = 0;//理论流量波动值
m_lastMaxWnd = 0;//最大发送窗口
m_AvageFlow = MIN_SENDWINDOWSIZE;//平均流量
m_expectwnd = MIN_SENDWINDOWSIZE;//预期理论窗口

m_bQuickRecover = FALSE;

m_WeekFlowTime = GetTimer()->GetTickCount();//流量下降时间
m_cntStatble = 0;//平稳周期计数
m_LastAckArrivedTime = GetTimer()->GetTickCount();//最后一次,数据到达时间
m_ackcheckdatatrtt = 0;
m_lostrate = 0;

m_LastSendToalCount = 0;
m_LastReSendCount = 0;

m_currenttimerbyts = 0;
m_acktimewatcher.Reset();
m_bew.Reset();
m_reb.Reset();
m_acktimewatcher.Reset();
}
void CWindowControl::IncreaseWndSize(BOOL bIncrease,BOOL bTimeOut,DWORD dwData)
{
Udx_IncreaseWndSize(bIncrease,bTimeOut,dwData);

if(m_sstresh <= MIN_SSTHRESH || m_sendwinsize <= MIN_SENDWINDOWSIZE)
{
m_sstresh = m_cfgwndsize*GetUdxGlobalCfg()->mss;
m_sendwinsize = MIN_SENDWINDOWSIZE;
m_LastMaxFlow = MIN_SENDWINDOWSIZE;
m_lastMaxWnd = MIN_SENDWINDOWSIZE;
m_AvageFlow = MIN_SENDWINDOWSIZE;
m_deltFlow = 0;
m_lostrate = 0;
m_acktimewatcher.Reset();
m_bew.Reset();
m_reb.Reset();
}

if(m_sendwinsize > m_cfgwndsize * GetUdxGlobalCfg()->mss)
{
m_sendwinsize = m_cfgwndsize * GetUdxGlobalCfg()->mss;
}

m_cntStatble += bIncrease;

if(m_pUdx->GetUdxCfg()->fixedwnd)
{
m_sendwinsize = m_pUdx->GetUdxCfg()->fixedwnd * GetUdxGlobalCfg()->mss;
}
assert(m_cfgwndsize >= 8);
assert(m_sendwinsize >= MIN_SENDWINDOWSIZE);
}
void CWindowControl::Udx_IncreaseWndSize( BOOL bIncrease,BOOL bTimeOut,DWORD dwData )
{……太长省略……

附件源码列表:

分享到:
赞(2)