一、实验目的
学习TCP的拥塞控制机制并了解TCP Tahoe和TCP Reno协议的运行机制。
二、实验背景知识——TCP基于窗口的拥塞控制策略
1.1 加法增加乘法减少(AIMD)窗口算法
TCP是Internet中最流行的端到端传输协议,为主机之间提供可靠按序的传输服务。在现有的TCP/IP协议体系下,TCP拥塞控制机制主要基于加法增加乘法减少(AIMD)算法。在该算法中主要用到三个窗口变量:
(1)拥塞窗口(cwnd):限定源端在拥塞控制中在一定时间内允许传送的最大数据量,是来自源端的流量控制。
(2)通告窗口(awnd):连接建立及传输过程中,接收端向源端通告的最大可接收速率,是来自接收端的流量控制。
(3)有效窗口(win):源端数据发送的实际窗口大小,限定为win=min(cwnd,awnd)。
由于计算机计算能力和存储能力的提高,通告窗口一般都比较大,因此当前发送窗口的大小大多数情况下等于拥塞窗口的大小。
AIMD的具体工作过程为:
(1)源端每收到一个ACK,拥塞窗口按下式增加:
Incr=MSS×(MSS/cwnd) (MSS为分组大小)
cwnd=cwnd+Incr
也就是,如果每个发出的分组都在最近的RTT(往返时延)时间内获得确认,源端就将cwnd增加1,即加法增加。当发生超时,TCP将超时看作拥塞的标志,并减小发送速率。每发生一次超时,源端重新计算拥塞窗口值:
cwnd=cwnd/2
也就是,一次超时,拥塞窗口值减为当前值的一半,即乘法减少。
1.2 TCP拥塞控制的四个阶段
a 启动阶段
当连接刚建立或超时时,进入慢启动阶段。
当新建TCP连接时,拥塞窗口(cwnd)被初始化为一个数据包大小。源端按cwnd大小发送数据,每收到一个ACK确认,就增加一个数据包发送量,这样慢启动阶段cwnd随RTT呈指数级增长。
慢启动采用逐渐增大cwnd的方法,可以防止TCP在启动一个连接时向网络发送过多的数据包而造成不必要的数据丢失和网络拥塞,并且它还能够避免采用单纯的AIMD算法造成的吞吐量增加过慢的问题。
为了防止cwnd的无限制增长引起网络拥塞,引入一个状态变量:慢启动阈值ssthresh。
当cwnd<ssthresh时,使用上述的慢启动算法,cwnd随RTT呈指数增长。
当cwnd>ssthresh时,使用拥塞避免算法,减缓cwnd的增长速度。
b 拥塞避免阶段
当TCP源端发现超时或收到3个相同的ACK确认帧时,即认为网络将发生拥塞,此时进入拥塞避免阶段。
在拥塞避免阶段,慢启动域值ssthresh将被设置为当前cwnd的一半,当发生超时时,cwnd被置为初始值1。此时,如果cwnd<ssthresh,TCP重新进入慢启动过程;如果cwnd> =ssthresh,则执行拥塞避免算法,即cwnd在每次收到一个ACK确认时只增加1/cwnd个数据包。拥塞避免阶段cwnd随RTT呈线性增长。
c、d 快速重传和快速恢复阶段
在拥塞避免阶段,当数据包超时时,cwnd被置为1,重新进入慢启动阶段,这会导致过大地减小发送窗口尺寸,降低TCP连接的吞吐量。因此,引入了快速重传和快速恢复机制。
在快速重传阶段,当源端收到3个或3个以上重复的ACK时,就判定数据包丢失,同时将ssthresh设置为当前cwnd的一半,并重传丢失的包,进入快速恢复阶段。
在快速恢复阶段,每收到重复的ACK,则cwnd加1;收到非重复ACK时,置cwnd=ssthresh,转入拥塞避免阶段;如果发生超时重传,则置ssthresh为当前cwnd的一半,cwnd=1,重新进入慢启动阶段。
这种方法避免了数据包超时后就重新进入慢启动阶段,提高了TCP连接的吞吐量。
三、实验步骤
1、安装NS2(本文基于Ubuntu16.04,18.04在安装NS2的编译过程中可能无法通过)
2、Tahoe和Reno实验
四、实验过程和结果
1、NS-2的安装(基于ubuntu16.04,18.04版本可能会编译不通过)
步骤一:安装ns2运行时需要的三个依赖包
sudo apt-get install build-essential
sudo apt-get install tcl8.5 tcl8.5-dev tk8.5 tk8.5-dev
sudo apt-get install libxmu-dev libxmu-headers
步骤二:下载安装包并编译安装(ns 2.35)
NS-2下载地址:https://sourceforge.net/projects/nsnam/files/(先下载再解压)
tar xvfz ns-allinone-2.35.tar.gz #解压压缩包
cd ns-allinone-2.35 #定位到所在文件夹,可以是自己的路径
接下来,修改ls文件,位置是:ns-2.35/linkstate/ls.h第137行:
void eraseAll() { erase(baseMap::begin(), baseMap::end()); }
改为:void eraseAll() { this->erase(baseMap::begin(), baseMap::end()); }
最后,执行:
./install
(博主安装成功的是ubuntu16.04,但是也测试了18.04版本的情况,这一步编译会不通过,建议使用前者)
步骤三:配置环境变量
修改当前用户环境变量(对应的文件为’/home/用户名’中的.bashrc,并非’/ns-allinone-2.35’底下的空白.bashrc,这大概是一个110行左右的文件),重新打开终端即可。
gedit ~/.bashrc # ~ 这个符号为当前用户根目录,即/home/用户名
直接在文档的最后面加上以下环境变量。注意tk和tcl版本,如果是8.15.19或者其他版本,请替换以下中8.5.10为你的版本号。
export NS_HOME=/home/用户名/ns-allinone-2.35 #这里是自己的安装路径,一定要填对
export PATH=$PATH:$NS_HOME/bin:$NS_HOME/tcl8.5.10/unix:$NS_HOME/tk8.5.10/unix
export LD_LIBRARY_PATH=$NS_HOME/otcl-1.14:$NS_HOME/lib
export TCL_LIBRARY=$NS_HOME/tcl8.5.10/library
步骤四:验证ns是否安装成功
关闭终端,重启终端,输入ns,出现%,说明ns2安装成功,如果环境变量配错的话,会提示“程序“ns”尚未安装。 此时请不要使用以下命令安装:sudo apt-get install ns2。会导致安装路径出现错误,环境更加难以配置。
此时应当继续在步骤4中修改环境变量。
步骤五:测试ns并验证nam是否安装成功
先重新打开一个新的终端:
cd ns-allinone-2.35/ns-2.35/tcl/ex/
ns simple.tcl
倘若弹出动画演示框,则证明ns完全安装正确,下面的问题也不需要看了,至此结束。
倘若提示nam没有安装(Ubutnu16.04没有出现这个问题),执行以下命令
cd ns-allinone-2.35/nam-1.15 #进入nam目录
./configure --with-tcl-ver=8.5 #重新配置
make #重新编译源码
make install #重新安装
命令行输入nam弹出nam console说明nam安装成功。
注意:如果通过“sudo apt-get install nam”安装的nam,执行nam命令会报异常“段错误,核心文件已转储”
遇到该问题可以“sudo apt-get remove –purge nam”卸载nam及其配置后按Step6安装。
安装部分参考:https://blog.csdn.net/circle2015/article/details/52490582
2、Tahoe和Reno算法的测试
仿真实验的网络结构图和源程序:
实验网络拓扑结构 和 链路参数配置 (FTP代表端施加恒定的流CBR)
本实验用到的程序成为“tcl脚本程序”。运行脚本程序,首先需要安装解释器。只要在第一步中,安装了ns2 allinone的压缩包的话,就已经安装了该tcl脚本语言的解释shell了(一般解释器被称作壳shell)。
关于它的介绍可以参考:http://blog.sina.com.cn/s/blog_5ff6e6ed0100drjl.html
本实验tcl程序:修改了指导书和其他博客上的一些错误,可以直接复制使用
if { $argc!=1 } {
puts"Usage:ns lab11.tcl tcpversion"
exit
}
set par1 [lindex $argv 0]
set ns [new Simulator]
#打开一个trace文件,用来记录数据报传送的过程
set nd [open $par1.tr w]
$ns trace-all $nd
#打开一个文件用来记录cwnd变化情况
set f0 [open cwnd-$par1.tr w]
#定义一个结束的程序
proc finish {} {
global ns nd f0 tcp
puts [format "average throughput:%.1f Kbps" \ [expr [$tcp set ack_]*([$tcp set packetSize_])*8/1000.0/10]]
$ns flush-trace
close $nd
close $f0
exit 0
}
#定义一个记录的程序
proc record {} {
global ns tcp f0
set now [$ns now]
puts $f0 "$now [$tcp set cwnd_]"
$ns at [expr $now+0.01] "record"
}
#产生传送结点,路由器r1和r2和接收结点
set n0 [$ns node]
set r0 [$ns node]
set r1 [$ns node]
set n1 [$ns node]
#建立链路
$ns duplex-link $n0 $r0 20Mb 1ms DropTail
$ns duplex-link $r0 $r1 1Mb 4ms DropTail
$ns duplex-link $r1 $n1 20Mb 1ms DropTail
#设置队列长度为18个封包大小
set queue 18
$ns queue-limit $r0 $r1 $queue
#根据用户的设置,指定TCP版本,并建立相应的Agent
if { $par1 == "Tahoe" } {
set tcp [new Agent/TCP]
set tcpsink [new Agent/TCPSink]
} elseif { $par1 == "Reno" } {
set tcp [new Agent/TCP/Reno]
set tcpsink [new Agent/TCPSink]
}
$ns attach-agent $n0 $tcp
$ns attach-agent $n1 $tcpsink
观察Tahoe版本的congestion window的变化情况
Tahoe算法是TCP的早期版本。它的核心思想是:让cwnd以指数增长方式迅速逼进可用信道容量,然后慢慢接近均衡。Tahoe包括3个基本的拥塞控制算法:“慢启动”、“拥塞避免”和“快速重传”。
(1)慢启动:避免了连接建立时突发数据流对网络的冲击。
初始设置cwnd为1,并按指数型方式增长,直至cwnd超过ssthresh。
当cwnd>=ssthresh时,Tahoe进入拥塞避免阶段。
(2)拥塞避免:限制传输过程中无限制的速率增长,避免由此可能导致的拥塞。
cwnd以线性方式增长。
如果发生超时或者连续收到3个重复ACK,Tahoe认为发生了拥塞。
对于超时,置ssthresh为当前拥塞窗口的一半,cwnd=1,转入慢启动。
如果收到3个连续ACK,则Tahoe进入快速重传阶段。
(3)快速重传:根据3个重复的应答报文来判断丢包,减少了超时重传的发生,加快了源端对拥塞的响应,使得拥塞能快速消除。立即重传丢失的分组,同时置ssthresh为当前拥塞窗口的一半,cwnd=1,转入慢启动。
Tahoe算法存在着不足之处:在收到3个重复ACK或在超时的情况下,Tahoe置cwnd为1,然后进入慢启动阶段。这一方面会引起网络的激烈振荡,另一方面大大降低了网络的利用率。
操作步骤:
定位到包含tcl脚本程序的文件夹下。执行指令:$ns 文件名.tcl Tahoe,观察结果:
此时,会在文件夹中生成两个文件,分别是cwnd-Tahoe.tr和Tahoe.tr,这两个文件反映的是NS-2模拟平台上的网络流量和拥塞窗口信息,其中,cwnd-Tahoe反映的是本次实验需要用到的拥塞窗口变化,需要之后用到并画图。
关于这两个文件,参考:https://www.cnblogs.com/zhangrui/archive/2012/03/05/2380788.html
安装画图软件,观察拥塞窗口的变化情况,使用命令安装Gnuplot,安装完毕后命令行输入“Gnuplot”,即可启动该软件。
执行以下代码:
Gnuplot>set title “Tahoe” #图像标题
Gnuplot>set xlabel “time” #横坐标时间
Gnuplot>set ylabel “cwnd” #纵坐标拥塞窗口
Gnuplot>set terminal gif #输出为gif格式
Gnuplot>set output “cwnd-Tahoe.gif” #输出文件名
Gnuplot>plot “cwnd-Tahoe.tr” with linespoints #画图,需要引号,引号内为上一步仿真结果的文件名。
可以看出,在TCP的Tahoe版本中,Congestion Windows值会呈现周期性的重复变化。刚开始采用Slow-Start开始,cwnd呈指数方式增长,当cwnd超过Ssthresh时就进入了Congestion Avoidance阶段。由于网络上的数据包不断增加,超过路由器的转发能力时,排队缓冲队列出现了溢出,路由器开始使用Drop-tail将数据包丢掉。当数据包丢失后,Tahoe版本TCP将ssthresh设为出现数据包丢失时的Windows值的1/2,并将cwnd值重设为1,并重新开始执行Slow-Start算法。
观察Reno版本的congestion window的变化情况
针对Tahoe算法的不足之处,1990年Jacobson在Tahoe的基础上提出了改进算法Reno。改进主要有两个方面:一是对于收到连续3个重复ACK,算法不经过慢启动,而直接进入拥塞避免阶段;二是增加了快速重传/快速恢复机制。具体实现过程为:
(1)收到三个重复的ACK,进入快速重传/快速恢复,此时ssthresh设置为当前拥塞窗口的一半。
(2)重传丢失的数据包,并置cwnd=cwnd+ndup(ndup为收到的重复ACK数)。
(3)发送新的数据包。
(4)当收到非重复的ACK时,cwnd=ssthresh。
(5)进入拥塞避免阶段。
从上面的过程可以看出,Reno在收到3个重复ACK后,就转入快速重传/快速恢复阶段;而遇到超时时,Reno和Tahoe一样进入慢启动阶段。
操作步骤:
定位到包含tcl脚本程序的文件夹下。执行指令:$ns 文件名.tcl Reno,观察结果:
和之前类似:此时,会在文件夹中生成两个文件,分别是cwnd-Reno.tr和Reno.tr,这两个文件反映的是NS-2模拟平台上的网络流量和拥塞窗口信息,其中,cwnd-Reno反映的是本次实验需要用到的拥塞窗口变化,需要之后用到并画图。
执行指令:
Gnuplot>set title “Reno” #图像标题
Gnuplot>set xlabel “time” #横坐标时间
Gnuplot>set ylabel “cwnd” #纵坐标拥塞窗口
Gnuplot>set terminal gif #输出为gif格式
Gnuplot>set output “cwnd-Reno.gif” #输出文件名
Gnuplot>plot “cwnd-Reno.tr” with linespoints #画图,需要引号,引号内为上一步仿真结果的文件名。
可以看出,在TCP的Reno版本中,开始阶段与Tahoe的表现一样。当网络上的数据包不断增加,超过路由器的转发能力时,排队缓冲队列出现了溢出,路由器开始使用Drop-tail将数据包后,Reno版本TCP将ssthresh和cwnd都设为出现数据包丢失时的Windows值的1/2,并开始执行Congestion Avoidance算法。
思考题
1、这个实验中只对cwnd的测量,没有对ACK帧的测量,能够能体现出“快重传”(不管在Tahoe还是在Reno中都有快重传这一特性的)这一特性吗?
在Tahoe中,不能体现快速重传这一特性,因为发送端在收到重复的ACK和发生超时都是采用慢启动策略,现象是一样的,所以不能体现;
在Reno中,能体现快速重传这一特性,可以看到图中的情况都是收到DupACK后然后快速重传的例子,因为发送端在收到重复的ACK后采用快速重传,而发生超时是采用慢启动策略,所以能体现。
2、为什么Reno版本在出现封包丢失后选择将ssthresh和cwnd都设为出现数据包丢失时的Windows值的1/2,而不是其它值呢?比如1/3,2/3?
(出自一篇讨论课文献,非本人答案,不是很确定)