DSS 代码分析【Reliable UDP之拥塞控制】

Darwin流媒体服务器中拥塞控制是在RTPBandwidthTracker类中定义并实现的。

1.在服务器启调用 RTPSession::Play给客户端播送音视频数据时,会调用RTPBandwidthTracker::SetWindowSize给拥塞窗口fCongestionWindow,fSlowStartThreshold设定初值。

void RTPBandwidthTracker::SetWindowSize(SInt32 clientWindowSize)
{
	//
	// Currently we only allow this info to be set once
	if (fClientWindow > 0)
		return;

	// call SetWindowSize once the clients buffer size is known
	// since this occurs before the stream starts to send

	fClientWindow = clientWindowSize;
	fLastCongestionAdjust = 0;

#if RTP_PACKET_RESENDER_DEBUGGING   
	//€ test to see what happens w/o slow start at beginning
	//if ( initSlowStart )
	//  qtss_printf( "ack list initializing with slow start.\n" );
	//else
	//  qtss_printf( "ack list initializing at full speed.\n" );
#endif

	if (fUseSlowStart)
	{
		fSlowStartThreshold = clientWindowSize * 3 / 4;

		//
		// This is a change to the standard TCP slow start algorithm. What
		// we found was that on high bitrate high latency networks (a DSL connection, perhaps),
		// it took just too long for the ACKs to come in and for the window size to
		// grow enough. So we cheat a bit.
		fCongestionWindow = clientWindowSize / 2;
		//fCongestionWindow = kMaximumSegmentSize;
	}
	else
	{
		fSlowStartThreshold = clientWindowSize;
		fCongestionWindow = clientWindowSize;
	}

	if (fSlowStartThreshold < kMaximumSegmentSize)
		fSlowStartThreshold = kMaximumSegmentSize;
}

2.RTPStream::ReliableRTPWrite在发送数据前会调用fResender.IsFlowControlled判断窗口是否已满,如果已满则延迟发包,代码如下

if (fResender.IsFlowControlled())
{
		err = QTSS_WouldBlock;
}else
{

		//
		// Assign a lifetime to the packet using the current delay of the packet and
		// the time until this packet becomes stale.
		fBytesSentThisInterval += inLen;
		fResender.AddPacket(inBuffer, inLen, (SInt32)(fDropAllPacketsForThisStreamDelay - curPacketDelay));

		(void)fSockets->GetSocketA()->SendTo(fRemoteAddr, fRemoteRTPPort, inBuffer, inLen);
}

//fBytesInList是已经发送的数据大小,fCongestionWindow代表当前拥塞窗口的大小
const Bool16 IsFlowControlled() { return ((SInt32)fBytesInList >= fCongestionWindow); }

3.已送数据fBytesInList大小更新:

   3.1 RTPStream::ReliableRTPWrite调用RTPPacketResender::AddPacket发包时会将发送包的大小累加到fBytesInList上。

          

void RTPPacketResender::AddPacket(void * inRTPPacket, UInt32 packetSize, SInt32 ageLimit)
{
......
      fBandwidthTracker->FillWindow(packetSize);
......
}

void FillWindow(UInt32 inNumBytes)
{
    fBytesInList += inNumBytes; fIsRetransmitting = false;
}
   3.2 RTPPacketResender::AckPacket收到确认包时会调用fBandwidthTracker->EmptyWindow(theEntry->fPacketSize),fBytesInList可用的大小变大。

         RTPPacketResender::ResendDueEntries()删除过期的包也会调用fBandwidthTracker->EmptyWindow;RTPPacketResender::RemovePacket在移除不被使用的包时,也

         会调用。

void RTPBandwidthTracker::EmptyWindow(UInt32 bytesIncreased, Bool16 updateBytesInList)
{
	if (bytesIncreased == 0)
		return;

	Assert(fClientWindow > 0 && fCongestionWindow > 0);

	if (fBytesInList < bytesIncreased)
		bytesIncreased = fBytesInList;

	if (updateBytesInList)
		fBytesInList -= bytesIncreased;

	// this assert hits
	Assert(fBytesInList < ((UInt32)fClientWindow + 2000)); //mainly just to catch fBytesInList wrapping below 0

	// update the congestion window by the number of bytes just acknowledged.

	if (fCongestionWindow >= fSlowStartThreshold)
	{
		// when we hit the slow start threshold, only increase the 
		// window for each window full of acks.
		fCongestionWindow += bytesIncreased * bytesIncreased / fCongestionWindow;
	}
	else
		//
		// This is a change to the standard TCP slow start algorithm. What
		// we found was that on high bitrate high latency networks (a DSL connection, perhaps),
		// it took just too long for the ACKs to come in and for the window size to
		// grow enough. So we cheat a bit.
		fCongestionWindow += bytesIncreased;


	if (fCongestionWindow > fClientWindow)
		fCongestionWindow = fClientWindow;

	//  qtss_printf("Window = %d, %d left\n", fCongestionWindow, fCongestionWindow-fBytesInList);
}
          

拥塞避免:为了防止fCongestionWindow增加过快而导致网络拥塞,所以需要设置一个慢开始门限fSlowStartThreshold状态变量。

当fCongestionWindow >= fSlowStartThreshold,使用拥塞控制算法,停用慢启动算法,fCongestionWindow缓慢的增长,比慢启动要慢的多

fCongestionWindow < fSlowStartThreshold,使用慢启动算法


当需要重传时(调用RTPPacketResender::ResendDueEntries),说明网络出现拥塞,就要把慢启动开始门限(fSlowStartThreshold)设置为设置为发送窗口的3/4,fCongestionWindow(拥塞窗口)设置为原来的1/2,然后在使用慢启动算法,这样做的目的能迅速的减少主机向网络中传输数据,使发生拥塞的路由器能够把队列中堆积的分组处理完毕,代码如下:

RTPPacketResender::ResendDueEntries--->RTPBandwidthTracker::AdjustWindowForRetransmit


void RTPBandwidthTracker::AdjustWindowForRetransmit()
{
	// this assert hits
	Assert(fBytesInList < ((UInt32)fClientWindow + 2000)); //mainly just to catch fBytesInList wrapping below 0

	// slow start says that we should reduce the new ss threshold to 1/2
	// of where started getting errors ( the current congestion window size )

	// so, we get a burst of re-tx becuase our RTO was mis-estimated
	// it doesn't seem like we should lower the threshold for each one.
	// it seems like we should just lower it when we first enter
	// the re-transmit "state" 
//  if ( !fIsRetransmitting )
//      fSlowStartThreshold = fCongestionWindow/2;

	// make sure that it is at least 1 packet
	if (fSlowStartThreshold < kMaximumSegmentSize)
		fSlowStartThreshold = kMaximumSegmentSize;

	// start the full window segemnt counter over again.
	fSlowStartByteCount = 0;

	// tcp resets to one (max segment size) mss, but i'm experimenting a bit
	// with not being so brutal.

	//curAckList->fCongestionWindow = kMaximumSegmentSize;

//  fCongestionWindow = kMaximumSegmentSize;
//  fCongestionWindow = fCongestionWindow / 2;  // half the congestion window size
	SInt64 theTime = OS::Milliseconds();
	if (theTime - fLastCongestionAdjust > 250) //调整周期为250
	{
		fSlowStartThreshold = fCongestionWindow * 3 / 4;   //慢开始门限设为拥塞窗口的3/4
		fCongestionWindow = fCongestionWindow / 2;         //拥塞窗口设为原来的1/2
		fLastCongestionAdjust = theTime;
	}

	/*
		if ( fSlowStartThreshold < fCongestionWindow )
			fCongestionWindow = fSlowStartThreshold/2;
		else
			fCongestionWindow = fCongestionWindow /2;
	*/

	if (fCongestionWindow < kMaximumSegmentSize)
		fCongestionWindow = kMaximumSegmentSize;

	// qtss_printf("Congestion window now %d\n", fCongestionWindow);
	fIsRetransmitting = true;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值