关于NewReno的理论部分学习可以参考拥塞控制算法(二)——Tahoe、Reno、NewReno、Vegas
在文件tcp-congestion-ops.cc文件中是TCP-NewReno的源码
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015 Natale Patriciello <natale.patriciello@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "tcp-congestion-ops.h"
#include "tcp-socket-base.h"
#include "ns3/log.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("TcpCongestionOps");
NS_OBJECT_ENSURE_REGISTERED (TcpCongestionOps);
TypeId
TcpCongestionOps::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpCongestionOps")
.SetParent<Object> ()
.SetGroupName ("Internet")
;
return tid;
}
TcpCongestionOps::TcpCongestionOps () : Object ()
{
}
TcpCongestionOps::TcpCongestionOps (const TcpCongestionOps &other) : Object (other)
{
}
TcpCongestionOps::~TcpCongestionOps ()
{
}
// RENO
NS_OBJECT_ENSURE_REGISTERED (TcpNewReno);
TypeId
TcpNewReno::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpNewReno")
.SetParent<TcpCongestionOps> ()
.SetGroupName ("Internet")
.AddConstructor<TcpNewReno> ()
;
return tid;
}
TcpNewReno::TcpNewReno (void) : TcpCongestionOps ()
{
NS_LOG_FUNCTION (this);
}
TcpNewReno::TcpNewReno (const TcpNewReno& sock)
: TcpCongestionOps (sock)
{
NS_LOG_FUNCTION (this);
}
TcpNewReno::~TcpNewReno (void)
{
}
/**
* \brief Tcp NewReno slow start algorithm
*
* Defined in RFC 5681 as
*
* > During slow start, a TCP increments cwnd by at most SMSS bytes for
* > each ACK received that cumulatively acknowledges new data. Slow
* > start ends when cwnd exceeds ssthresh (or, optionally, when it
* > reaches it, as noted above) or when congestion is observed. While
* > traditionally TCP implementations have increased cwnd by precisely
* > SMSS bytes upon receipt of an ACK covering new data, we RECOMMEND
* > that TCP implementations increase cwnd, per:
* >
* > cwnd += min (N, SMSS) (2)
* >
* > where N is the number of previously unacknowledged bytes acknowledged
* > in the incoming ACK.
*
* The ns-3 implementation respect the RFC definition. Linux does something
* different:
* \verbatim
u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)
{
u32 cwnd = tp->snd_cwnd + acked;
if (cwnd > tp->snd_ssthresh)
cwnd = tp->snd_ssthresh + 1;
acked -= cwnd - tp->snd_cwnd;
tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);
return acked;
}
\endverbatim
*
* As stated, we want to avoid the case when a cumulative ACK increases cWnd more
* than a segment size, but we keep count of how many segments we have ignored,
* and return them.
*
* \param tcb internal congestion state
* \param segmentsAcked count of segments acked
* \return the number of segments not considered for increasing the cWnd
*/
uint32_t
TcpNewReno::SlowStart (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (segmentsAcked >= 1)
{
tcb->m_cWnd += tcb->m_segmentSize;
NS_LOG_INFO ("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh " << tcb->m_ssThresh);
return segmentsAcked - 1;
}
return 0;
}
/**
* \brief NewReno congestion avoidance
*
* During congestion avoidance, cwnd is incremented by roughly 1 full-sized
* segment per round-trip time (RTT).
*
* \param tcb internal congestion state
* \param segmentsAcked count of segments acked
*/
void
TcpNewReno::CongestionAvoidance (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (segmentsAcked > 0)
{
double adder = static_cast<double> (tcb->m_segmentSize * tcb->m_segmentSize) / tcb->m_cWnd.Get ();
adder = std::max (1.0, adder);
tcb->m_cWnd += static_cast<uint32_t> (adder);
NS_LOG_INFO ("In CongAvoid, updated to cwnd " << tcb->m_cWnd <<
" ssthresh " << tcb->m_ssThresh);
}
}
/**
* \brief Try to increase the cWnd following the NewReno specification
*
* \see SlowStart
* \see CongestionAvoidance
*
* \param tcb internal congestion state
* \param segmentsAcked count of segments acked
*/
void
TcpNewReno::IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (tcb->m_cWnd < tcb->m_ssThresh)
{
segmentsAcked = SlowStart (tcb, segmentsAcked);
}
if (tcb->m_cWnd >= tcb->m_ssThresh)
{
CongestionAvoidance (tcb, segmentsAcked);
}
/* At this point, we could have segmentsAcked != 0. This because RFC says
* that in slow start, we should increase cWnd by min (N, SMSS); if in
* slow start we receive a cumulative ACK, it counts only for 1 SMSS of
* increase, wasting the others.
*
* // Incorrect assert, I am sorry
* NS_ASSERT (segmentsAcked == 0);
*/
}
std::string
TcpNewReno::GetName () const
{
return "TcpNewReno";
}
uint32_t
TcpNewReno::GetSsThresh (Ptr<const TcpSocketState> state,
uint32_t bytesInFlight)
{
NS_LOG_FUNCTION (this << state << bytesInFlight);
return std::max (2 * state->m_segmentSize, bytesInFlight / 2);
}
Ptr<TcpCongestionOps>
TcpNewReno::Fork ()
{
return CopyObject<TcpNewReno> (this);
}
} // namespace ns3
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("TcpCongestionOps");
关于这部分的c++语言结构看不懂可以参考:C++类构造函数学习
定义一个名为TcpCongestionOps的TypeId类,并定义其GetTyped函数。
在GetTyped函数内部,定义全局属性TypeId为"ns3::TcpCongestionOps"。set其父类为Object,所属分组为Internet(应该是上层文件夹名)。无其他属性Attribute。
NS_OBJECT_ENSURE_REGISTERED (TcpCongestionOps);
TypeId
TcpCongestionOps::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpCongestionOps")
.SetParent<Object> ()
.SetGroupName ("Internet")
;
return tid;
}
设置TcpCongestionOps类的构造方法TcpCongestionOps(void)。并赋值Object为空。
TcpCongestionOps::TcpCongestionOps () : Object ()
{
}
设置TcpCongestionOps类的构造方法TcpCongestionOps(const TcpCongestionOps &other)。并传入名为other的输入的索引(对other值更改则调用函数时传入的变量也更改。参考链接:C++学习——&)。
TcpCongestionOps::TcpCongestionOps (const TcpCongestionOps &other) : Object (other)
{
}
设置TcpCongestionOps类的析构函数TcpCongestionOps()。相当于把该对象分解掉,释放内内存
TcpCongestionOps::~TcpCongestionOps ()
{
}
类似第三部分定义TcpCongestionOps类的同样操作,定义TcpNewReno。
// RENO
NS_OBJECT_ENSURE_REGISTERED (TcpNewReno);
TypeId
TcpNewReno::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpNewReno")
.SetParent<TcpCongestionOps> ()
.SetGroupName ("Internet")
.AddConstructor<TcpNewReno> ()
;
return tid;
}
TcpNewReno::TcpNewReno (void) : TcpCongestionOps ()
{
NS_LOG_FUNCTION (this);
}
TcpNewReno::TcpNewReno (const TcpNewReno& sock)
: TcpCongestionOps (sock)
{
NS_LOG_FUNCTION (this);
}
TcpNewReno::~TcpNewReno (void)
{
}
uint32_t
TcpNewReno::SlowStart (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (segmentsAcked >= 1)
{
tcb->m_cWnd += tcb->m_segmentSize;
NS_LOG_INFO ("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh " << tcb->m_ssThresh);
return segmentsAcked - 1;
}
return 0;
}
声明一个TcpSocketState类型的变量的指针,该指针名叫tcb。
Ptr是一个声明指针的模板类,C++中为固定用法。Ptr模板类定义如下:
template <typename T> class Ptr
T为传入值,类型为typename,可见是一个类型名称。Ptr类为template <typename T>class,即T类型的模板类。
tcb是TcpSocketState类型的变量的指针,TcpSocketState类型中有m_cWnd属性。tcb->m_cWnd是指通过tcb来索引该属性。
Log部分不再阐述,this << tcb << segmentsAcked的意思与cout相似。
算法的关键部分是:
if (segmentsAcked >= 1)
{
tcb->m_cWnd += tcb->m_segmentSize;
return segmentsAcked - 1;
}
当ack大于1时,将拥塞窗口的大小进行调整,增加值为段大小。
由此可见,就是对拥塞窗口增加一个段的大小。可见NewReno在慢启动阶段是线性增长。
void
TcpNewReno::CongestionAvoidance (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (segmentsAcked > 0)
{
double adder = static_cast<double> (tcb->m_segmentSize * tcb->m_segmentSize) / tcb->m_cWnd.Get ();
adder = std::max (1.0, adder);
tcb->m_cWnd += static_cast<uint32_t> (adder);
NS_LOG_INFO ("In CongAvoid, updated to cwnd " << tcb->m_cWnd <<
" ssthresh " << tcb->m_ssThresh);
}
}
static_cast<double>的含义与double类似,但是运行时间比double更快。
忽略Log部分,算法的关键部分是:
adder是拥塞避免阶段加在cwnd上的增量。该增量是max(1.0,段大小的平方除以cwnd)。
void
TcpNewReno::IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (tcb->m_cWnd < tcb->m_ssThresh)
{
segmentsAcked = SlowStart (tcb, segmentsAcked);
}
if (tcb->m_cWnd >= tcb->m_ssThresh)
{
CongestionAvoidance (tcb, segmentsAcked);
}
TcpNewReno使用IncreaseWindow把SlowStart和CongestionAvidance封装起来。
拥塞窗口在ssThresh之下时,为慢启动阶段,窗口大于ssTresh时,为拥塞避免阶段。
GetName和Fork函数不再赘述,下面来看ssTresh的设置。
uint32_t
TcpNewReno::GetSsThresh (Ptr<const TcpSocketState> state,
uint32_t bytesInFlight)
{
NS_LOG_FUNCTION (this << state << bytesInFlight);
return std::max (2 * state->m_segmentSize, bytesInFlight / 2);
}
ssTresh为max (2, 拥塞时的cwnd/2)