目录
一、源码
二、详解
1.定义日志和命名空间
2.注册Typeld类:TcpVegas和GetTypeId方法的实现
3.构造函数和析构函数
4.TcpVegas类中成员函数
(1) Fork函数
(2) PktsAcked函数
(3) EnableVegas函数
(4) DisableVegas函数
一、源码
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2016 ResiliNets, ITTC, University of Kansas
*
* 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
*
* Author: Truc Anh N. Nguyen <annguyen@ittc.ku.edu>
*
* James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
* ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets
* Information and Telecommunication Technology Center (ITTC)
* and Department of Electrical Engineering and Computer Science
* The University of Kansas Lawrence, KS USA.
*/
#include "tcp-vegas.h"
#include "tcp-socket-state.h"
#include "ns3/log.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("TcpVegas");
NS_OBJECT_ENSURE_REGISTERED (TcpVegas);
TypeId
TcpVegas::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpVegas")
.SetParent<TcpNewReno> ()
.AddConstructor<TcpVegas> ()
.SetGroupName ("Internet")
.AddAttribute ("Alpha", "Lower bound of packets in network",
UintegerValue (2),
MakeUintegerAccessor (&TcpVegas::m_alpha),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("Beta", "Upper bound of packets in network",
UintegerValue (4),
MakeUintegerAccessor (&TcpVegas::m_beta),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("Gamma", "Limit on increase",
UintegerValue (1),
MakeUintegerAccessor (&TcpVegas::m_gamma),
MakeUintegerChecker<uint32_t> ())
;
return tid;
}
TcpVegas::TcpVegas (void)
: TcpNewReno (),
m_alpha (2),
m_beta (4),
m_gamma (1),
m_baseRtt (Time::Max ()),
m_minRtt (Time::Max ()),
m_cntRtt (0),
m_doingVegasNow (true),
m_begSndNxt (0)
{
NS_LOG_FUNCTION (this);
}
TcpVegas::TcpVegas (const TcpVegas& sock)
: TcpNewReno (sock),
m_alpha (sock.m_alpha),
m_beta (sock.m_beta),
m_gamma (sock.m_gamma),
m_baseRtt (sock.m_baseRtt),
m_minRtt (sock.m_minRtt),
m_cntRtt (sock.m_cntRtt),
m_doingVegasNow (true),
m_begSndNxt (0)
{
NS_LOG_FUNCTION (this);
}
TcpVegas::~TcpVegas (void)
{
NS_LOG_FUNCTION (this);
}
Ptr<TcpCongestionOps>
TcpVegas::Fork (void)
{
return CopyObject<TcpVegas> (this);
}
void
TcpVegas::PktsAcked (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked,
const Time& rtt)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked << rtt);
if (rtt.IsZero ())
{
return;
}
m_minRtt = std::min (m_minRtt, rtt);
NS_LOG_DEBUG ("Updated m_minRtt = " << m_minRtt);
m_baseRtt = std::min (m_baseRtt, rtt);
NS_LOG_DEBUG ("Updated m_baseRtt = " << m_baseRtt);
// Update RTT counter
m_cntRtt++;
NS_LOG_DEBUG ("Updated m_cntRtt = " << m_cntRtt);
}
void
TcpVegas::EnableVegas (Ptr<TcpSocketState> tcb)
{
NS_LOG_FUNCTION (this << tcb);
m_doingVegasNow = true;
m_begSndNxt = tcb->m_nextTxSequence;
m_cntRtt = 0;
m_minRtt = Time::Max ();
}
void
TcpVegas::DisableVegas ()
{
NS_LOG_FUNCTION (this);
m_doingVegasNow = false;
}
void
TcpVegas::CongestionStateSet (Ptr<TcpSocketState> tcb,
const TcpSocketState::TcpCongState_t newState)
{
NS_LOG_FUNCTION (this << tcb << newState);
if (newState == TcpSocketState::CA_OPEN)
{
EnableVegas (tcb);
}
else
{
DisableVegas ();
}
}
void
TcpVegas::IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
if (!m_doingVegasNow)
{
// If Vegas is not on, we follow NewReno algorithm
NS_LOG_LOGIC ("Vegas is not turned on, we follow NewReno algorithm.");
TcpNewReno::IncreaseWindow (tcb, segmentsAcked);
return;
}
if (tcb->m_lastAckedSeq >= m_begSndNxt)
{ // A Vegas cycle has finished, we do Vegas cwnd adjustment every RTT.
NS_LOG_LOGIC ("A Vegas cycle has finished, we adjust cwnd once per RTT.");
// Save the current right edge for next Vegas cycle
m_begSndNxt = tcb->m_nextTxSequence;
/*
* We perform Vegas calculations only if we got enough RTT samples to
* insure that at least 1 of those samples wasn't from a delayed ACK.
*/
if (m_cntRtt <= 2)
{ // We do not have enough RTT samples, so we should behave like Reno
NS_LOG_LOGIC ("We do not have enough RTT samples to do Vegas, so we behave like NewReno.");
TcpNewReno::IncreaseWindow (tcb, segmentsAcked);
}
else
{
NS_LOG_LOGIC ("We have enough RTT samples to perform Vegas calculations");
/*
* We have enough RTT samples to perform Vegas algorithm.
* Now we need to determine if cwnd should be increased or decreased
* based on the calculated difference between the expected rate and actual sending
* rate and the predefined thresholds (alpha, beta, and gamma).
*/
uint32_t diff;
uint32_t targetCwnd;
uint32_t segCwnd = tcb->GetCwndInSegments ();
/*
* Calculate the cwnd we should have. baseRtt is the minimum RTT
* per-connection, minRtt is the minimum RTT in this window
*
* little trick:
* desidered throughput is currentCwnd * baseRtt
* target cwnd is throughput / minRtt
*/
double tmp = m_baseRtt.GetSeconds () / m_minRtt.GetSeconds ();
targetCwnd = static_cast<uint32_t> (segCwnd * tmp);
NS_LOG_DEBUG ("Calculated targetCwnd = " << targetCwnd);
NS_ASSERT (segCwnd >= targetCwnd); // implies baseRtt <= minRtt
/*
* Calculate the difference between the expected cWnd and
* the actual cWnd
*/
diff = segCwnd - targetCwnd;
NS_LOG_DEBUG ("Calculated diff = " << diff);
if (diff > m_gamma && (tcb->m_cWnd < tcb->m_ssThresh))
{
/*
* We are going too fast. We need to slow down and change from
* slow-start to linear increase/decrease mode by setting cwnd
* to target cwnd. We add 1 because of the integer truncation.
*/
NS_LOG_LOGIC ("We are going too fast. We need to slow down and "
"change to linear increase/decrease mode.");
segCwnd = std::min (segCwnd, targetCwnd + 1);
tcb->m_cWnd = segCwnd * tcb->m_segmentSize;
tcb->m_ssThresh = GetSsThresh (tcb, 0);
NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd <<
" ssthresh=" << tcb->m_ssThresh);
}
else if (tcb->m_cWnd < tcb->m_ssThresh)
{ // Slow start mode
NS_LOG_LOGIC ("We are in slow start and diff < m_gamma, so we "
"follow NewReno slow start");
TcpNewReno::SlowStart (tcb, segmentsAcked);
}
else
{ // Linear increase/decrease mode
NS_LOG_LOGIC ("We are in linear increase/decrease mode");
if (diff > m_beta)
{
// We are going too fast, so we slow down
NS_LOG_LOGIC ("We are going too fast, so we slow down by decrementing cwnd");
segCwnd--;
tcb->m_cWnd = segCwnd * tcb->m_segmentSize;
tcb->m_ssThresh = GetSsThresh (tcb, 0);
NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd <<
" ssthresh=" << tcb->m_ssThresh);
}
else if (diff < m_alpha)
{
// We are going too slow (having too little data in the network),
// so we speed up.
NS_LOG_LOGIC ("We are going too slow, so we speed up by incrementing cwnd");
segCwnd++;
tcb->m_cWnd = segCwnd * tcb->m_segmentSize;
NS_LOG_DEBUG ("Updated cwnd = " << tcb->m_cWnd <<
" ssthresh=" << tcb->m_ssThresh);
}
else
{
// We are going at the right speed
NS_LOG_LOGIC ("We are sending at the right speed");
}
}
tcb->m_ssThresh = std::max (tcb->m_ssThresh, 3 * tcb->m_cWnd / 4);
NS_LOG_DEBUG ("Updated ssThresh = " << tcb->m_ssThresh);
}
// Reset cntRtt & minRtt every RTT
m_cntRtt = 0;
m_minRtt = Time::Max ();
}
else if (tcb->m_cWnd < tcb->m_ssThresh)
{
TcpNewReno::SlowStart (tcb, segmentsAcked);
}
}
std::string
TcpVegas::GetName () const
{
return "TcpVegas";
}
uint32_t
TcpVegas::GetSsThresh (Ptr<const TcpSocketState> tcb,
uint32_t bytesInFlight)
{
NS_LOG_FUNCTION (this << tcb << bytesInFlight);
return std::max (std::min (tcb->m_ssThresh.Get (), tcb->m_cWnd.Get () - tcb->m_segmentSize), 2 * tcb->m_segmentSize);
}
} // namespace ns3
二、详解
1.定义日志和命名空间
#include "tcp-vegas.h" //包含TCP Vegas算法的头文件。
#include "tcp-socket-state.h" //包含TCP套接字状态的头文件
#include "ns3/log.h" //包含NS-3日志功能的头文件
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("TcpVegas"); //定义了一个日志组件,用于记录日志信息。
NS_OBJECT_ENSURE_REGISTERED (TcpVegas);
2.注册Typeld类:TcpVegas和GetTypeId方法的实现
TypeId
TcpVegas::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpVegas") //设置类的名称为TcpVegas 位于ns3命名空间下
.SetParent<TcpNewReno> () //设置TcpVegas的父类为TcpNewReno
.AddConstructor<TcpVegas> () //添加TcpVegas类的构造函数 创建相关对象
.SetGroupName ("Internet") //将TcpVegas分类到"Internet"组下
.AddAttribute ("Alpha", "Lower bound of packets in network",
UintegerValue (2),
MakeUintegerAccessor (&TcpVegas::m_alpha),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("Beta", "Upper bound of packets in network",
UintegerValue (4),
MakeUintegerAccessor (&TcpVegas::m_beta),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("Gamma", "Limit on increase",
UintegerValue (1),
MakeUintegerAccessor (&TcpVegas::m_gamma),
MakeUintegerChecker<uint32_t> ())
;
return tid; //返回TcpVegas的TypeId对象
}
AddAttribute方法用于添加类的属性,这些属性可以在NS-3的配置系统中设置和获取。
- "Alpha"、"Beta"和"Gamma"是TcpVegas算法的三个参数,它们分别控制算法的行为:
- "Alpha":网络中数据包的下界,初始值为2。
- "Beta":网络中数据包的上界,初始值为4。
- "Gamma":增加的极限,初始值为1。
- UintegerValue:设置属性的初始值。
- MakeUintegerAccessor:创建一个访问器,用于访问和修改属性值。
- MakeUintegerChecker<uint32_t>():创建一个检查器,确保属性值是有效的无符号整数。
该段代码在NS-3中注册TcpVegas类,并设置其属性和行为,使得TcpVegas可以在NS-3的模拟中被创建和配置。
3.构造函数和析构函数
TcpVegas::TcpVegas (void) //默认构造函数
: TcpNewReno (), //TcpVegas通过调用其父类TcpNewReno的默认构造函数来进行初始化
m_alpha (2),
m_beta (4),
m_gamma (1),
m_baseRtt (Time::Max ()),
m_minRtt (Time::Max ()),
m_cntRtt (0),
m_doingVegasNow (true),
m_begSndNxt (0)
{
NS_LOG_FUNCTION (this); //日志记录构造函数的调用,this指向当前对象的指针
}
TcpVegas::TcpVegas (const TcpVegas& sock) //复制构造函数
: TcpNewReno (sock),
m_alpha (sock.m_alpha),
m_beta (sock.m_beta),
m_gamma (sock.m_gamma),
m_baseRtt (sock.m_baseRtt),
m_minRtt (sock.m_minRtt),
m_cntRtt (sock.m_cntRtt),
m_doingVegasNow (true),
m_begSndNxt (0)
{
NS_LOG_FUNCTION (this);
}
TcpVegas::~TcpVegas (void) //析构函数
{
NS_LOG_FUNCTION (this);
}
默认构造函数:
m_alpha (2)
,m_beta (4)
,m_gamma (1)
:这些行初始化TcpVegas
算法的参数alpha
、beta
和gamma
,分别设置为2、4和1。m_baseRtt (Time::Max ())
,m_minRtt (Time::Max ())
:将基础往返时间(baseRtt
)和最小往返时间(minRtt
)初始化为最大时间值,表示它们尚未被设置。m_cntRtt (0)
:初始化往返时间计数器(cntRtt
)为0。即用于计数自连接建立以来观测到的RTT样本数量。m_doingVegasNow (true)
:初始化标志doingVegasNow
为true
,表示Vegas算法默认是启用的。m_begSndNxt (0)
:初始化发送下一个序列号(begSndNxt
)为0。- 日志系统记录构造函数的调用,
this
指向当前对象的指针。
复制构造函数:用于创建一个与另一个TcpVegas
对象sock
相同的新对象。
: TcpNewReno (sock)
:表明TcpVegas
复制构造函数首先调用其父类TcpNewReno
的复制构造函数来复制父类成员。- 接下来,复制
sock
对象中的alpha
、beta
、gamma
、baseRtt
、minRtt
和cntRtt
成员变量的值到新对象。 m_doingVegasNow (true)
和m_begSndNxt (0)
:与默认构造函数类似,初始化doingVegasNow
为true
和begSndNxt
为0。- const TcpVegas& sock:参数
sock
是TcpVegas
类型的对象引用,它指向一个已经存在的对象
4.TcpVegas类中成员函数
(1) Fork函数
Ptr<TcpCongestionOps>
TcpVegas::Fork (void)
{
return CopyObject<TcpVegas> (this);
}
Fork
函数用于创建当前TcpVegas
对象的一个副本,并返回这个副本的智能指针;Ptr<TcpCongestionOps>
是一个智能指针,指向TcpCongestionOps
类型的对象。Ptr
是NS-3中用于管理对象生命周期的智能指针模板类,而TcpCongestionOps
是一个抽象基类,代表TCP拥塞控制操作。CopyObject<TcpVegas> (this)
:这是NS-3中用于复制对象的模板函数,它创建了当前对象的一个副本,并返回一个指向新对象的智能指针。这里的this
指针指向当前的TcpVegas
对象。
(2) PktsAcked函数
void
TcpVegas::PktsAcked (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked,
const Time& rtt)
{
NS_LOG_FUNCTION (this << tcb << segmentsAcked << rtt);
if (rtt.IsZero ())
{
return;
}
m_minRtt = std::min (m_minRtt, rtt);
NS_LOG_DEBUG ("Updated m_minRtt = " << m_minRtt);
m_baseRtt = std::min (m_baseRtt, rtt);
NS_LOG_DEBUG ("Updated m_baseRtt = " << m_baseRtt);
// Update RTT counter
m_cntRtt++;
NS_LOG_DEBUG ("Updated m_cntRtt = " << m_cntRtt);
}
- PktsAcked函数在收到ACK时被调用,用于更新最小 RTT(m_minRtt)和基础 RTT(m_baseRtt),并统计 RTT 样本的数量 (m_cntRtt)。
- 如果 rtt 为零,则直接返回,不进行任何操作。这通常是为了避免处理无效的数据(例如无效的 ACK 或零延迟的情况)。
std::min
是 C++ 标准库中的一个函数模板,它返回两个参数中的较小值。- m_minRtt 表示当前连接或当前窗口内的最小 RTT。在每次收到 ACK 包时,如果新的 RTT 比当前记录的 m_minRtt 小,就会更新 m_minRtt。
std::min(m_minRtt, rtt) 会选择 m_minRtt 和当前 RTT 中较小的一个,并将其赋值给 m_minRtt。确保 m_minRtt 始终保持为最小的 RTT 值。 - m_baseRtt 用于记录连接过程中观察到的最小 RTT(通常是在连接的初期或网络的稳定阶段)。这代表了网络的基准延迟(即理想的延迟)。
- 与 m_minRtt 类似,m_baseRtt 会更新为当前 RTT 和已有的 m_baseRtt 中的最小值。这可以确保 m_baseRtt 始终为连接期间的最小延迟。
- m_cntRtt 是 RTT 样本的计数器。每次收到 ACK 包时,都会增加 m_cntRtt 的值,表示新的 RTT 样本被记录。
baseRtt 和 minRtt的区别见:
TCP Vegas拥塞控制算法——baseRtt 和 minRtt的区别-CSDN博客
(3) EnableVegas函数
void
TcpVegas::EnableVegas (Ptr<TcpSocketState> tcb)
{
NS_LOG_FUNCTION (this << tcb);
m_doingVegasNow = true;
m_begSndNxt = tcb->m_nextTxSequence;
m_cntRtt = 0;
m_minRtt = Time::Max ();
}
void
TcpVegas::DisableVegas ()
{
NS_LOG_FUNCTION (this);
m_doingVegasNow = false;
}
EnableVegas
函数用于启用Vegas算法。m_doingVegasNow
标志被设置为true
,表示Vegas算法现在被激活。m_begSndNxt
被设置为下一个传输序列号,用于跟踪Vegas周期的开始。- 将
m_cntRtt
成员变量重置为0,在每个新的Vegas周期开始时重置这个计数器。 - 将
m_minRtt
重置为Time::Max()
,即最大可能的时间值;用于在新的Vegas周期中重新寻找最小的RTT值。
(4) DisableVegas函数
void TcpVegas::DisableVegas ()
{
NS_LOG_FUNCTION (this);
m_doingVegasNow = false;
}
DisableVegas
函数用于禁用Vegas算法。m_doingVegasNow
标志被设置为false
,表示Vegas算法现在被禁用。