Skip to main content
A cup of beer
  1. Posts/

计算机网络-传输层复习笔记

·325 words·2 mins

传输层概述 #

传输层为不同host上的进程提供逻辑通信。所谓逻辑通信就是虽然这两个进程实体不直接相连,但是看起来好像直接可以通信。

传输层协议 #

发送方:从进程处获取消息,并分段成若干segment,对每个segment打包成TPDU,丢给网络层发出去。

接收方:从网络层拿到若干TPDU,组合恢复成消息,并传给进程。

和网络层的区别 #

传输层和网络层都提供逻辑通信。传输层是进程的逻辑通信,网络层是host的逻辑通信。

理解 #

Lee和Han是两个家庭,这两个家庭里的孩子会相互写信。Lee和Han都会负责把孩子要寄的信给邮局,并从邮局拿信分发给孩子。

在这里,房子就是host,孩子们就是进程,邮局就是网络层,Lee和Han就是传输层。

TCP和UDP #

TCP可以保证可靠连接、按序交付。可以负责拥塞、流量控制。连接建立和拆除。

UDP是尽力而为的协议。没有做可靠性的支持,只是完成传输层的基本功能。

多路复用和多路分用 #

Socket的地位 #

Socket是传输层到应用层的一个接口。

就像其他层级之间的接口那样,Socket也是担任这样一个角色。由于Socket产生的作用能明显地被我们应用,所以特别拿出来作编程实验。

多路复用 #

传输层从多个Socket接受数据,分别封装segment,丢给网络层的过程。

即:传输层把消息从应用层传给网络层的过程就是所谓多路复用。

多路分用(多路解复用) #

类比多路复用,可以得出对偶结论:传输层把消息从网络层交给应用层的过程就是多路分用。

“多路解复用”非常形象:即把复合在一起的若干个Segment分开,分别送到对应的Socket。

传输层从网络层拿到一系列segment,分别交给正确的Socket。

在段格式中的体现 #

无论是TCP还是UDP,其段头部都是以“源端口”、“目的端口”开始的。

UDP无连接的多路分用 #

段头只需要有SP和DP。传输层直接根据DP把消息送给正确的Socket即可。并用SP作为返回信息的依据。

TCP面向连接的多路分用 #

TCP的段头不仅有SP和DP,还有SIP和DIP(即伪首部中的)。利用这些,如果两个不同客户机用完全相同的SP和DP请求通信,则仍然可以区分出两者。

UDP - User Datagram Protocol 用户数据报协议 #

相对于IP,增加了多路复用和多路分用机制。增加了简单的错误校验。

为什么链路层已经能保证比特流正确传输了,UDP仍然需要错误校验机制 #

因为传输层作为端到端的协议,谁也不知道中间点到点的链路层到底有没有这种机制。并且路由器转发也可能出错。

UDP有错误校验,但是没有错误恢复 #

因此是不可靠传输和无序交付。

因为IP是"Best Effort",所以UDP也是。毕竟,UDP只是对IP的简单升级。

如果要使用UDP进行可靠传输,则需要程序员在应用层自己实现。

UDP相对于TCP的好处 #

延迟低——无需建立连接,无需控制差错

没有拥塞控制——上层可以更自由地控制发送时间和速率(对于需要掌控数据发送时间和速率的应用,UDP更合适)

空间小——头部仅8B,相比20B的TCP会小很多

实现简单——无需维持连接

UDP的应用 #

流媒体,DNS,SNMP

可靠数据传输(Rdt) #

什么是可靠 #

不错、不丢、不乱;

不出错(0-1反转)、不丢包、保持顺序。

控制信息的流向 #

在应用层和RDT之间,是单向的。因为对于应用层来说,只要操作一次就能达到完全正确的结果,故不需要多次信息流动。

在RDT和UDT之间,是双向的。因为为了保障可靠传输,一定会用到双向的控制信息交换。

各种可靠数据传输协议的前提假设 #

假设只进行单向数据传输,即发送方和接收方是明确的。(但是控制信息能双向流动。)

乌托邦协议 (Rdt1.0 可靠信道上的Rdt) #

直接收发即可。

停-等协议I( Rdt2.0 只会产生位错误的信道上的Rdt) #

在这种信道上,虽然不是可靠信道,但是仍能保证按序到达、包不丢失。

引入校验和——需要引入Checksum,以便接收方能够发现错误。

引入控制消息——需要引入接收方的ACK/NAK机制,以便显式地通知发送方是正确还是错误。

引入重传机制——发送方收到NAK,则重新发送。

停-等协议II (Rdt2.1,Rdt2.2 只会产生位错误的信道上的,ACK会坏掉的情况下的Rdt) #

Rdt2.0的缺陷在于如果ACK/NAK坏掉了,则发送方不知道怎么处理,会进入死锁。(状态机无法转移。)如何解决ACK或NAK坏掉?不能尝试纠错,因为花销较大;不能添加额外控制信息(例如:“你说什么”),因为该信息也可以像ACK一样坏掉;解决方式是只要ACK/NAK坏了,发送方就重传上一个分组。

引入序列号——重传意味着重复。因此引入序列号0、1(Seq Number)来避免重复。接收方如果发现序列号重复,则说明是重传分组,则丢弃即可。

Rdt2.2相比Rdt2.1,去掉了NAK。然后用ACK+Seq 取而代之。Rdt2.1虽然有Seq Number,但是在ACK时不指定是确认了哪个序号。Rdt2.2 在ACK时指定序号,即可代替NAK。具体做法是,只要确认上一个序号即可代表NAK。

停-等协议III (Rdt3.0 会发生丢失和位错误,不会乱序的信道上的Rdt) #

相比Rdt2.2,这里又多了一种“丢失”错误。如果丢失,无论是ACK丢失了还是数据本身就丢失,对方没收到,双方都会等待从而死锁。

引入计时器——如果发送方等待ACK超时,则重新发送上一分组。超时可能是ACK真的丢失了,也可能是网络阻塞延迟。如果是后者,就可能导致重复。但是Rdt2.X中的Seq Num完全能应付。所以Rdt3.0 成功诞生。

缺点:Rdt3.0的效率非常低。在1Gbps的线路上,只能做到33KB/s的速率。因此说明,协议如果不够好,会使得线路资源浪费。

从流水线协议到滑动窗口 #

相比Rdt的停-等协议,流水线协议是一种全新的思路。但是需要注意,停-等协议的分组一定是按序到达的,但是流水线协议的分组会出现乱序。

相比Rdt3.0,由于发送时间更多了,然而红色框子(即从刚开始发送到第一个ACK到达)的时间长度不变,故一次发多少个包,效率就提升几倍。滑动窗口协议使得同一时刻在线路上跑的分组或ACK数量>1了。然而Rdt1.0-3.0的这个数字都是1.

引入更大的Seq Number范围——因为需要区分更多的包

引入发送方和接收方缓存——因为有更多的等待确认的分组。只有确认完成,才可以把它们从缓存中删除,否则都有重传的可能。

引入窗口——窗口是固定大小的一段区间,其中包含若干连续序列号。在窗口左侧是已经确认的序列号,在窗口里面,nextseqnum指针左侧是已经发送但还没确认的序列号,在nextseqnum指针及其右侧是还未发送(即可用的)序列号。窗口的含义是当前活动序列号的范围。每次有新的确认了的序列号,窗口就向前移动,直到窗口内nextseqnum指针左侧的区域都是还未确认的序列号。窗口的大小限定了目前的活动序列号最多有多少个。在最坏情况下,待确认的序列号的总数最大值就是窗口大小。

后退N滑动窗口协议(GBN) #

引入累积确认——ACK n代表序列号<=n的分组都被ACK。注意,累积确认不是用于偷懒的。它仍然是每收到一个包就ACK一下。累积确认在正常收发时,其行为和非累积确认完全一致。它和后者的区别只有在出现错误的时候才体现出来。即:出错时,告诉发送方,从哪个序号之后就不靠谱了。

引入GBN——如果空中编号n的分组TimeOut,则重发编号>=n的所有分组。

引入发送方的缓存——存那些发出但是还没被确认的。为后退N重传做好准备。

发送方:如果目前不存在活动的可用的序号,则拒绝上层的发送请求。

接收方:只需要维持唯一的期望序号。如果当前收到的序号是这个期望值,则期望序号++,并ACK之(这里的ACK和非累积确认是一模一样的)。如果不是这个值,则直接丢弃,并重新发送ACK期望序号的上一个序号(这里终于体现累积确认和普通确认的不同了)。这里的丢弃很有讲究:因为这样,GBN的接收方就不需要任何缓存了。乱序到达被解决:因为如果包提早来了,但是显然序列号不是期望的nextseqnum,所以会直接丢掉。

选择重传滑动窗口协议(SR) #

GBN存在严重缺陷:乱序到达的分组都被浪费了。之后它们会被再次传输,非常浪费。在SR中,引入接收方窗口。这意味着接收方可以对待接受的序列号对应的分组进行管理,比GBN更聪明。由于GBN只是去关注下一个待接受的序列号,所以也可以认为是:GBN的接收方窗口大小为1.

取消GBN的累积确认机制,改为单独确认机制——这样,可以使每个包只要成功接收就是有效的。不存在GBN那种虽然到了但是被无情丢弃的情况。

引入接收方的缓存和接收方窗口——为乱序到达的分组提供空间。注意,接收方窗口和发送方窗口的base并不同步。它们也并不知道对方的窗口目前是什么状态。

使用多个计时器——GBN只用单个计时器,但是SR使用多个计时器,即对每个序列号都对应开一个计时器,计时器可以对每个特定的序号都计时。更有针对性。

网络拥塞 #

太多主机发送太快或发送了太多数据,导致网络一时间无法处理。

拥塞的表现 #

分组丢失——来的分组太多,router的缓冲区装不下(可以理解为对于router的流量控制失衡)

延迟指数级增大,有效转发占比下降——router缓存里的东西太多,想要转发需要排很长的队。

引起上游路由器转发能力的浪费——当拥塞的路由器丢失分组,也就否定了上游所有路由器的劳动成果。

什么是拥塞控制 #

站在全局的角度,降低网络的负载。大家可能都得作出一定牺牲。

理想路由器的拥塞 #

假定路由器拥有无限的缓存容量,但是其输出带宽是C。由于缓存容量无穷,故必然不会发生丢失。因此只需考虑带宽问题。

则当其输入带宽在C以下,其输出都是线性增长,且等于输入带宽。当其输入在C及其以上,输出就稳定在C了。此时路由器达到最大吞吐率。

但是,当输入带宽接近C,delay就会突然指数级上升。并当输入=C时,delay为无穷大。因为当输入带宽接近输出带宽,加之router的处理时间,router中的分组会越积越多。当输入带宽=输出带宽,意味着一定有大部分的分组永远都不会被发出去。

引出拥塞的代价1:增大delay。

实际路由器的拥塞 #

此时考虑一个拥有有限缓存,有限输出带宽C 的路由器。由于缓存有限,故如果输入过多,必然会发生丢失现象。

如果发送方能够随时得知路由器的缓存剩余量,在有剩余的时候重新发送,则仍然能保证吞吐量曲线是y=x的直线(x<=C)(因为如果>C,则缓冲区必然满,发送方也不可能发了)。采用这种方式发送,则完全可以避免丢失现象:因为缓冲区永远能放下接收到的分组!!!

但是实际上发送方并不能得知这一点。因此当发送量到达一定程度,丢失率会显著提升,则重发量相应提升。但是,单位时间内跑的分组的数量是有限的,所以实际有效吞吐率会随着重发分组数量占比的提升而大幅度下降。最终,在发送速率=C时,有效输出量一定会低于C不少。

引出拥塞的代价2:丢失现象。这也导致有效转发占比下降。

多跳实际路由器网络的拥塞 #

此时考虑一个由多个实际路由器构成的网络。当多条线路在某个路由器上进行竞争,如果数据过多,总和向C接近,则路由器开始拥塞。由于是实际路由器,其丢失率急剧上升。

但是,这里丢掉的分组不只是这个拥塞路由器的损失,这还相当于否定了其上游所有路由器的劳动成果(即其上游的所有路由器都白对这个分组进行处理了,白忙活了)。这导致上游所有路由器的等效有效输出量急剧下降。

当上游服务器的传输能力被浪费,网络的净传输能力就更差了。输入速率越大,丢失就越明显,这种效应就越明显。当输入速率继续增大,所有的资源都被浪费。此时没有人能从网络中正确接收到信息了,输出速率=0,网络彻底瘫痪。

引出拥塞的代价3:当分组被Drop,任何其上游的路由器的传输能力全部浪费。

TCP - 传输控制协议 #

TCP是点对点通信,不支持多播和组播。

TCP是全双工通信。

TCP是面向连接的通信。这种连接由双方负责维持,与途经的路由器无关。(因为TCP是传输层协议,而router是网络层。)

TCP使用Rdt传输,且采用流水线机制,利用拥塞控制和流量控制动态调整窗口大小。TCP采用了介于GBN和SR之间的一种流水线滑动窗口机制。它在发送方和接收方都有缓存(在这一点上更像SR)。

TCP的Seq和ACK #

项目 解释
Seq Number TCP段的第一个字节的编号(字节为单位)。在TCP建立连接时,双方的第一个序列号都是随机选取,然后双方在建立连接的过程中交换相关信息,于是获得彼此的base序列号。以后以此为base,以字节为单位进行编号即可。
ACK 下一个期望接收的序列号(累积确认机制,这里更像GBN)

TCP的Rdt概述 #

由于IP是“best effort”,不能保证可靠传输,所以TCP要负责可靠传输的实现。

TCP使用流水线滑动窗口机制,采用GBN中的累积确认和GBN中的单计时器。

TCP的重传 #

TCP在什么情况下会重传?由于确认机制类似于GBN,故和GBN一样,如果收到重复ACK或计时器超时,即重传。

但是,由于TCP在接收方也设有缓冲区,所以和SR一样,不需要重传N个了。只需要重传引起重传的那个序列号即可。

即:重传原因类似GBN,重传方式类似SR。

TCP的计时器 #

像GBN一样,TCP使用单个计时器。该计时器在重传时、ACK时会重新启动。如果发送时计时器没启动,则也需要主动启动它。

TCP的快速重传机制 #

在TCP的实现中,如果发生超时,则下次设定计时器时,计时器时间将加倍。如果分组丢失,则需要等很长时间才能重新发送。

因此,可以通过观察接收方的ACK的规律来判断是否要立即重传这个分段。即:如果接收方迟迟收不到某个分段,则根据累积确认机制,不管收到什么序号,他都会一直返回ACK没收到的那个分段之前一个的序号。如果这种ACK出现了三次了,那么发送方就可以直接判断这个分段丢了,可以立即重传。不用等到计时器超时了。

TCP流量控制 #

为什么要进行流量控制?如果发送方速度过快,以至于填满接收方的缓冲区,使得接收方来不及处理,这种情况称为淹没接收方的缓冲区。

因此,TCP段头中的接收窗口大小RcvWindow字段中表示自己的接收缓冲区还剩余多少空间。(因为序号是以字节为单位,所以字节就是Seq num,故空间剩余就是接收窗口序号剩余。)

如果发送方看到接收方给自己的段头中,RcvWindow=0,则不再发送。为避免进入死锁,发送方之后还得给接收方发一个很小的段,然后接收方回复新的RcvWindow的值(此时应该能空出来一些了)。

TCP三次握手 #

序号 解释
第一次 不包含数据部分。客户端发送SYN=1的段,seq为随机指定一个最初的Seq Num。
第二次 不包含数据部分。服务端发送SYN=1,ACK=1的段,seq为随机指定一个最初的Seq Num,ack为刚才客户端seq+1.
第三次 可以包含数据部分。客户端发送ACK=1的段。seq为刚才客户端seq+1.ack为刚才服务端seq+1.

注意,这里ack都是seq+1的原因是如果不带数据,那就是默认+1的。注意!

注意,服务器端的资源分配(例如缓冲区建立)是在第二次握手时。

网络攻击——如果第三次握手,客户机没有发送,则服务器会把自己的缓冲区资源保留一段时间,然后确认不会建立连接了才释放掉(这段时间,服务器会重传第二次握手段)。因此如果有大量客户机同时给服务器发送第一次握手,却不发送第三次握手,就可以攻击服务器。

TCP四次挥手 #

序号 解释
1 客户机主动向服务器发送FIN段
2 服务器发送ACK段
3 服务器发送FIN段,并等待ACK,直到等到即可关闭连接
4 客户机发送ACK段

TCP拥塞控制 #

拥塞是由于发送方太快地发送了过多的数据导致路由器来不及处理。因此,拥塞控制的最基本手段就是控制发送速率

TCP的拥塞窗口 Congestion Window #

这个窗口的定义是所有已经发送但是还没被确认的序号。

通过拥塞窗口动态调整发送速率 #

发送速率=拥塞窗口大小/RTT (B/s).由于RTT看作恒定,所以可以通过调整拥塞窗口大小来动态调整发送速率。如果发生拥塞,把窗口开小点即可。

如何感知网络拥塞,从而作出拥塞窗口大小调整 #

很简单,如果出现分组的丢失,就说明网络拥塞了。

具体说,如果出现超时或者3个以上ACK(即快速重传被触发),就说明网络拥塞了。这时直接调整拥塞窗口大小即可。

AIMD方法调整拥塞窗口大小 #

AIMD,即Additive Increase & Multiplicative Decrease,加性增,乘性减。

每过一个RTT,就把拥塞窗口增大一个MSS(Max Segment Size,最大段长度)。如果某时刻探测到拥塞,则立即让拥塞窗口大小减半。

基本思想:试探性地提速,并在出现问题时以最大速度止损。

图像:以RTT为单位作为横轴,以拥塞窗口大小(单位:B)作为纵轴,可以看出AIMD的图像为锯齿状。

SS方法调整拥塞窗口大小 #

SS,即Slow Start,慢启动。初始状态时,拥塞窗口大小被初始化一个MSS的大小(因为拥塞窗口能达到的最小值也就是MSS了)。

思想:慢启动,快增长。即:初始速率很低,必然远低于引起loss的程度。但是可以让他很快地往上增长。即使用指数型增长。

一开始初始化拥塞窗口为MSS,之后每个RTT,如果上一轮的ACK都收到了,则将拥塞窗口翻倍并发送新的。

Threshold、慢启动状态和拥塞避免状态 #

Threshold被定义为上次发生Loss时,拥塞窗口大小的一半。

如果当前窗口大小<Threshold,TCP处于慢启动状态,其拥塞窗口大小以SS方式增长;

如果当前窗口大小>Threshold,TCP处于拥塞避免状态,其拥塞窗口大小以AIMD方式增长。如果此时出现Loss,则Threshold更新为本次Loss发生时,拥塞窗口大小的一半。且,此时拥塞窗口调整的方式依据情况而定,并不一定时AIMD方式了。

TCP Series 1 Tahoe 和 TCP Series 2 Reno #

这两者的区别在于当出现Loss事件,拥塞窗口大小的变化。Tahoe是直接降低到MSS,Reno是降低到Threshold。对于Tahoe来说,就是回到慢启动状态,对于Reno来说,就是直接到拥塞控制状态(从Threshold开始重新以AIMD方式增长)。

不同Loss事件会导致不同的TCP Series #

如果出现3个Duplicated ACKs, 则说明网络仍然可以传递数据,拥塞不严重,此时采用Reno方法。

如果出现Timeout,则说明网络几乎彻底瘫痪,连ACK都回不来!!此时采用Tahoe方法,彻底释放压力,重新来过。

TCP拥塞控制算法 #
Threshold = ?
CongWin = MSS;
while(1){
    while(CongWin<Threshold && 没有loss出现)	
    	发送Segment,并且CongWin*=2;
	while(没有loss出现)
        发送Segment,并且CongWin+=1;
    Threshold/=2;
    if(收到3 Duplicated ACKs) CongWin = Threshold;
    if(Timeout) CongWin = MSS
}

TCP的公平性 #

多个TCP连接进行竞争,其吞吐率最终会收敛于对路由器瓶颈带宽的平分。因此是公平的。具体见图集。拿两个TCP来说,纵轴是连接2,横轴是连接1,平面中任意点都可以代表他们两个的速率情况,且这个点一定位于直线Y=-X+C以下(C是路由器输出带宽,或者说是瓶颈带宽。)这个点会作AIMD增长,一旦增长到Y=-X+C以上,就会向原点方向下降。多次以往,这个点就容易落到Y=X上,或者说会收敛于Y=X上。而Y=X就是吞吐率的平分线。

但是TCP和UDP一同连接就会不公平。因为后者不讲武德,没有流量控制和拥塞控制,但是前者却会自动限流。

TCP的性能(平均吞吐率) #

设出现Timeout时,拥塞窗口大小为W,则平均吞吐率是0.75W/RTT.

这是由最大吞吐率W/RTT和减半后的吞吐率W/2RTT平均而来。