程序员不得不会的计算机科班知识——计算机网络篇(中)

966 阅读29分钟

计算机科班知识整理专栏系列文章:

【1】程序员不得不会的计算机科班知识——操作系统篇(上)
【2】程序员不得不会的计算机科班知识——操作系统篇(下)
【3】程序员不得不会的计算机科班知识——数据库原理篇(上)
【4】程序员不得不会的计算机科班知识——数据库原理篇(下)
【5】程序员不得不会的计算机科班知识——数据结构与算法篇(上)
【6】程序员不得不会的计算机科班知识——数据结构与算法篇(下)
【7】程序员不得不会的计算机科班知识——软件工程篇(上)
【8】程序员不得不会的计算机科班知识——软件工程篇(中)
【9】程序员不得不会的计算机科班知识——软件工程篇(下)
【10】程序员不得不会的计算机科班知识——编译原理篇(上)
【11】程序员不得不会的计算机科班知识——编译原理篇(中)
【12】程序员不得不会的计算机科班知识——编译原理篇(下)
【13】程序员不得不会的计算机科班知识——计算机网络篇(上)
【14】程序员不得不会的计算机科班知识——计算机网络篇(中)
【15】程序员不得不会的计算机科班知识——计算机网络篇(下)

第四章 传输层

4.1 传输层服务和协议

4.1.1 传输层服务概述

  • 在不同主机上运行的应用程序进程之间提供逻辑通信
  • 只运行在终端系统里面:
    • 发送端:将应用程序消息分成segment,传递到网络层
    • 接收端:将片段重新组装成消息message,传递给应用程序层
  • 与网络层的区别:网络层是在两个主机之间建立传输通道,而传输层则是在两个进程之间建立传输通道。
  • 传输层涉及的协议:TCP、UDP

4.1.2 TCP和UDP的区别

UDPTCP
是否连接无连接面向连接
是否可靠不可靠传输,不使用流量控制和拥塞控制可靠传输(数据顺序和正确性),使用流量控制和拥塞控制
连接对象个数支持一对一,一对多,多对一和多对多交互通信只能是一对一通信
传输方式面向报文面向字节流
首部开销首部开销小,仅8字节首部最小20字节,最大60字节
适用场景适用于实时应用,例如视频会议、直播适用于要求可靠传输的应用,例如文件传输

总结:

TCP:按序传输、拥塞控制、流量控制、连接建立

UDP:乱序到达、速度快、尽最大努力传输、可能丢包

但无论是TCP还是UDP,都无法做到延迟保障、带宽保障

4.2 分解与复用(multiplexing/demultiplexing)

  • 支持众多应用进程共用同一个传输层协议,并能够将接收到的数据准确交付给不同的应用进程,称为传输层的多路复用与多路分解,简称复用与分解,或复用与分用。 e8fc2dc4313d10b39d634b3bad49be9.jpg
  • 端口号:只属于传输层,用来区分计算机不同进程的标识号。TCP收到上面传过来的message,然后封装一个包头,并且把目的地的端口号封装在包头里面,然后找到对应端口号的进程。范围为(0,65535],分为两个区间,(0,1023]--众所周知的端口号区间,分配给服务器进程、[1024,65535]--临时的端口号区间,可随机分配。
    • 80:http
    • 443:https
    • 21/20:ftp
    • 25:SMTP
    • 110:pop3
  • TCP和UDP这两个协议所负责的端口号:TCP——>(0,64k],UDP——>(0,,64k]。但不会发生冲突,因为协议不同。
  • TCP和UDP两者复用时的区别:

4.3 无连接传输UDP

4.3.1 UDP简单引入

  • UDP:User Datagram Protocol(用户数据报协议
  • UDP中,接收方和发送方不握手,即不建立连接。
  • 每个UDP段独立处理:
  • UDP在流媒体应用程序中适用(因为容错、速率敏感)

4.3.2 UDP的包头

  • source port:发送方的端口号(16bits)——>因为端口号范围为(0,65535]
  • dest port:接收方的端口(16bits)——>因为端口号范围为(0,65535]
  • length:代表UDP数据包的总字节数(包括包头与包体)——>所以最大的包只能是64k,UDP协议不能发大包,否则可能丢包(最小的包是定长8个bits的,只有包头,没有包体,用于通知)。TCP其实也不能,但是TCP是把大包分割成小包来发,所以可以发大包。
  • checksum:校验码
  • C语言中,可用unsighner short类型来保存端口号(因为16bits)

4.4 可靠数据传输原则(RDT,reliable data transfer)

4.4.1 Rdt存在意义及原理

  • 可靠数据传输rdt原理: 现实中很多信道是不可靠的,很可能会发生丢包(由缓存溢出)等错误,所以我们需要使用技术手段来使传输变得可靠     
  • 可靠的含义:不错,不丢,不乱(重复分组)     
  • Rdt 协议:对应用层,传输层,链路层都很重要。例如在传输层使用了UDP协议,而网络应用需要可靠传输,则需要使用可靠传输协议。     
  • Rdt 被列为网络十大问题之一。
  • 基本FSM(有限状态机)结构:

4.4.2 Rdt 1.0 协议

  • 特点:发送方与接收方都只有一个状态
  • 考虑条件:底层信道完全可靠条件下:(理想条件,实际不存在),具体解释如下:

不会发生错误     

不会丢弃分组     

发送方与接收方的FSM 独立

  • 发送方:一个状态,等待上层调用。若上层调用,则产生rdt_send事件,创建packet活动,调用信道上的udt_send(),发送分组,可确定百分百发送,然后回到之前状态,继续等待调用
  • 接收方:一个状态,等待下层调用
  • 当传入一个分组,rdt_rcv接收,extract提取,交付给上层deliver_data
  • 具体的FSM解释图如下:

4.4.3 Rdt 2.0 协议

PS:该协议也叫做ARQ协议 Automatic Repeat reQuest

  • 特点: 等待上层调用,等待ACK或NAK控制信息
  • 考虑条件:基于Rdt 1.0的不可行性,底层信道可能反转分组中的位,解决方法如下:    

Frist:利用校验和检测位错误    

Second:如何从错误中恢复(引入的新机制):                    

确认机制ACK:接收方显式告知发送方分组已正确接收                     

NAK:接收方显式告知发送方分组有错, 发送方接收到NAK后,重传分组

  • 发送方:若上层调用,则产生rdt_send事件,创建packet活动并加入校验盒,调用信道上的udt_send(),发送分组。同时进入等待ACK/NAK状态,若为NAK,则重传分组,继续等待ACK/NAK,一直处于该状态,直到传回ACK才进入等待调用状态。
  • 接收方:当传入一个分组,rdt_rcv接收并且进行判断,如果没有错误extract提取,交付给上层deliver_data并返回ACK,如果发生错误,则直接返回NAK,并处于等待接收状态。
  • 具体的FSM解释图如下:

4.4.4 Rdt 2.1 协议

  • 特点:发送方和接收方都有四个状态,比Rdt 2.0中多了两个序列号状态
  • 考虑条件:基于 rdt_2.0的缺陷: 如果ACK/NAK消息发生错误/被破坏,就会进入死锁。 解决方式如下:

可以使用重传来解决问题,但是重传会导致重复分组问题,所以要解决重复分组问题:      

发送方给每个分组增加序列号     

同样使用停-等协议

  • 发送方:等待上层调用,序列号为0,若调用,若上层调用,则产生rdt_send事件,创建packet活动(此处加入序列号)并加入校验盒,调用信道上的udt_send(),发送分组。同时进入等待ACK/NAK状态,若为NAK,则重传分组,继续等待ACK/NAK,一直处于该状态。若 传回ACK,则进入等待调用状态,并改变序列号为1。

  • 接收方:当传入一个分组,rdt_rcv接收并且进行判断,如果分组没有错误,并且期望收到分组序列号与当前序列号相同,则extract提取,交付给上层deliver_data并返回ACK,如果发生错误,则直接返回NAK,并处于等待接收状态; 若接收分组没错,序列号不匹配,则必须发一个ACK,表示正确接收。  

  • 发送方和接收方FSM解释图分别如下:

4.4.5 Rdt 2.2 协议

  • 特点:基于Rdt 2.1 ,使用无NAK消息机制
  • 并无多余考虑条件
  • 与Rdt 2.1不同之处如下:

无NAK消息协议:     

只需要在ACK消息中显示的加入被确认分组的序列号,

接收方通过ACK告知最后一个被正确接收的分组     

发送方收到重复ACK后,采取重传当前分组

  • 具体FSM解释图如下:

4.4.6 Rdt 3.0 协议

4.4.6.1 Rdt 3.0 协议的介绍

  • 特点:多了一个时钟设置
  • 考虑条件:之前所有Rdt 协议只假设了信道可能发生的一种错误,bit error:位的错误。不会丢失分组,现在假设信道既可能丢失分组,又可能发生错误。具体解决方法如下:

First:发送方等待合理的时间,若timeout,没收到ACK,也没收到NAK,则重传。

Second:但此时会导致一个问题:分组或ACK只是延迟了,则会引起重复问题,则需要加入序列号

  • 发送方和接收方:在Rdt 2.0的基础上增加一个时钟,其它没有任何变化。给出发送方的FSM解释图:

4.4.6.2 常用的Rdt 3.0的实例情况

1. 没有任何丢失

2. 丢包

3. 丢失ACK

4. Timeout

4.4.6.3 Rdt 3.0 协议的性能

  • Rdt 3.0能够正确工作,但性能很差。低效率的原因是采取的是停-等操作。

翻译:传输延迟Dtrans = 数据包宽度L/带宽R = 8000bits/10^9 bits/s = 8 microsecs(微秒)

发送方的利用率Usender:发送方忙着发送的时间的百分比

Usender = Drans/(RTT+Drans)

如果RTT = 30ms,每30ms传输1KB的数据包,则此时Usender = 0.008/30.008 = 0.00027,利用率很小!!! 此时在1Gbps的带宽上,吞吐量竟然只有33kb/s!!!

4.4.7 Rdt 3.0 协议的改造——GBN和SR

4.4.7.1 引入

Rdt 3.0 协议性能很差,需要改造,即go-Back-N(GBN)和selective repeat(SR)。

4.4.7.2 GBN和SR特点

  • 相同点:都是流水线协议,支持一次往外扔N个包,N有一个最大值。如果达到最大值,还是得确认返回。

  • 发生丢包时的处理区别(GBN有累积确认ACK机制)

  • 不同应用场景下效率区别:

    • 乱序包过多:SR效率更高(因为能缓存下来,不用重新发送)
    • 确认包ACK大量丢失:由于GBN的累计确认,若Sender收到ackN,则ACK1到ACK(N-1)说明都收到了;此时SR只能说明收到1号包和100号包,可能导致重发包,此时GBN的效率就高多了。

4.4.7.3 GBN和SR是如何运作的——窗口滑动问题

  • 上面说到SR中,Receiver会将乱序包缓存在buffer里面,但如果此时接收到的包不是窗口里面规定的包,如4/5/6/7号包,则不会将这个包进行缓存。
  • 说明SR中,Receiver也有窗口,跟Sender的窗口的size一样大,但是position不一定一样。例如,Sender收到Ack1后,Sender的窗口才向后滑动;Receiver在发出Ack1后,窗口就可以向后滑动了。总的来说,都是最前面的窗口完成相应处理后,窗口才会往前滑动。例如:滑动窗口的大小为4,发送数据包0,1,2,3,窗口满了,停止发送,等待确认,其中数据包2丢失,依次确认数据包0,1,同时窗口向后移,依次发送数据包4,5,当数据包2超时,发送方会再次发送数据包2,同时缓存乱序到达的3,4,5,当接收完数据包2后,滑动窗口后移,发送方继续发送数据包:
  • GBN中,Receiver的窗口大小始终为1。Sender收到Ack1后,Sender的窗口才向后滑动;Receiver在发出Ack1后,窗口就可以向后滑动了。总的来说,都是最前面的窗口完成相应处理后,窗口才会往前滑动。例如:窗口大小为4,发送方发送数据包0,1,2,3,然后进入等待状态,其中数据包2丢失,接收方返回Ack0,1,窗口滑动继续发送包4,5,此时包2计时超时,默认数据包2没有收到,按照GBN,发送方重新发送数据包2,3,4,5。这里可以看出数据包重复了:

4.4.7.4 关于GBN和SR中,窗口大小问题

  • 窗口大小比序列号数还要大,绝对会出错;等于时也会出错;窗口大小一定要小于序列号大小。

  • 例如:(出错原因:会出现向上递交相同序列号的包,无法区分是重发包还是新发的包) c3c3632bd27643ce8cfafdaf9afbb72.jpg

  • Wsize+1 = seq,GBN就不会出错了,但SR仍然会出错

  • SR中:

    • 序列号空间大小与窗口尺寸需满足
    • 若序号位数k位(SR协议),发送窗口和接收窗口尺寸最大是2**-1,即序号空间一半。
    • 避免发生接收序号重叠,出现重复分组

4.5 面向连接的传输TCP

4.5.1 TCP引入

  • 利用了GBN的累计确认与SR的乱序包缓存机制
  • Request For Comments,缩写为RFC,是由互联网工程任务组(IETF)发布的一系列备忘录。文件收集了有关互联网相关信息,以及UNIX和互联网社群的软件文件,以编号排定。RFC793指的是TCP对应的文件编号。
  • TCP的特点:
    • 一个发送者发送的包只能被一个接收者接收,unicast,one-to-one。(与这种机制不同的有广播机制(broadcast,one-to-all)、组播机制(multicast,one-to-group))。
    • 可靠的,按序传输的,“无消息边界”
    • 拥塞控制算法、流量控制算法
    • 全双工(full duplex data,即发送方和接收方建立好连接后,可以互相通讯;MSS:最大的segment大小)

4.5.2 TCP的包头

  • source port:发送方的端口号(16bits)——>因为端口号范围为(0,65535]
  • dest port:接收方的端口(16bits)——>因为端口号范围为(0,65535]
  • sequence number:序列号(对接收方receiver很重要,用来解决乱序问题)——32bits:
    • 字节流的一个编号,向对方发送的字节的编号,用来标识TCP源端口向目标端口发生的字节流,用来确认身份
  • acknowledgement number:确认号(对发送方sender很重要,用来进行流量控制)——32bits
    • 用来记录收到了哪些字节,指下一次期待收到的数据的序列号,发送端收到了这个应答之后,可以认为这个序号之前的数据都正常接收,如:

4.5.3 TCP的MISS

bd23104299aabacae8e26e1375c12ef.jpg

  • 上图的意思:

    link链路层的frame最大部分为MTU(frame' payload max size),最常见为1500字节,因为包含20字节的包头,数据段则为1500-20 = 1480字节;则导致下一层网络层最大为1480字节,其中包含20字节的包头,则导致下一层中的TCP的segment' palyload(包体)最大为MSS,常见为1500-Head-Head-Head = 1460字节。

4.5.4 TCP往返时间,超时(RTO,TCP等待确认的时间长度)

  • TCP与之前提到的RDT一样,都采用超时/重传机制来处理报文段的丢失问题。那如何设置TCP超时值呢?答案肯定是超时间隔必须大于该连接的往返时间(RTT)。但是RTT是变化的。如果超时间隔设置太短会造成不成熟的超时,这就会导致不必要的重传;而时间间隔太长则对数据段丢失响应慢。
  • 既然RTT是变化的,那么问题又来了,如何估计RTT呢:
    • 样本RTT:测量从报文段发送到收到确认的时间,这里不考虑重传的报文段即不为重传的报文段计算样本RTT。
    • 注意:在任意时刻,仅为一个已发送的但目前尚未被确认的报文段估计样本RTT,从而产生一个接近于每个RTT的新样本RTT值。也就是随机抽取一个当作样本值。
    • 但是RTT变化,RTT样本值也会随之变化,因此需要一个样本RTT均值,对收到的样本RTT根据以下公式进行均值处理:
      • 样本RTT均值=(1-a)* 样本RTT均值+a * 样本均值。
      • 上述均值计算被称为:指数加权移动平均,典型的:a=0.125。

4.5.5 TCP可靠数据传输的原因

  • TCP在IP不可靠服务之上创建rdt即可靠数据传输服务。采取的方法主要有:

    1. 流水线技术处理报文段。
    2. 采用累计确认机制。
    3. TCP使用单个重发定时器。
    4. 超时事件和重复确认都会触发重发。
  • TCP发送方做的事件主要有如下:

    1. 从应用程序接收数据:将数据封装在一个报文段中,并把该报文段交给IP。每一个报文段都包含一个序号,序号是数据段中第一个数据字节在字节流中的位置编号;如果没有启动定时器则启动定时器;设置超时间隔。
    2. 超时:重发导致超时的数据段;重新开启定时器。
    3. 收到确认:来自接收方确认报文段的到答,如果确认了还没有确认的数据段,则更新还没有确认的状态;若还有未完成的数据段,那么重新开始定时器。
  • TCP的重传触发:

    1. 计时器超时重传
    2. 重复ack来立刻触发重传(一般是这种,前面那种时间太长)

4.5.6 三次握手和四次挥手

4.5.6.1 三次握手

4.5.6.1.1 三路握手的过程

三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接受能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

刚开始客户端处于Closed的状态,服务端处于Listen状态。进行三次握手:

  • 第一次握手: 客户端给服务端发一个SYN报文,这时首部中的同部位SYN = 1, 同时选择一个初始序号seq = x 。TCP规定,SYN报文段(即SYN = 1的报文段)不能携带数据,但要消耗一个序号。这时TCP客户进程进入SYN-SENT(同步发送)状态。
  • 第二次握手: 服务器收到客户端的SYN报文之后,会以自己的SYN报文作为应答,服务端在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时也为自己选择一个初始序号 seq = y 。这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务器进程进入SYN-RCVD(同步收到)状态。
  • 第三次握手: TCP客户进程在收到服务端的确认后还要向服务端做出确认。确认报文段的ACK置为1,确认号ack = y + 1,而自己的序号seq = x + 1(初始为seq = x,所以第二个报文段要+1)。ACK报文段可以携带数据,不携带数据则不消耗序号。这时TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。当服务端收到客户端的确认后,也进入ESTABLISHED状态。
4.5.6.1.2 三次握手的作用
  • 第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

  • 第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接受能力是否正常。

  • 第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

TCP三次握手的原因有以下几点:
1、避免历史错误连接的建立,减少通信双方不必要的资源消耗。
2、帮助通信双方获取初始化序列号,保证数据包传输的不重不丢,以及传输顺序的正确性。
3、确认双方的接收能力和发送能力是否正常。

如果只用两次握手,那么客户端无法确认服务器端是否收到了自己的ACK包,也无法知道服务器端是否可以正常发送数据。这样就可能导致客户端一直等待服务器端发送数据,而服务器端却没有发送数据的意图,造成资源浪费和连接超时。

如果用四次或更多次握手,那么就会增加通信延迟和网络开销,降低传输效率。而且多余的握手并不能提供更多的信息或保障。

所以三次握手是一种折中的设计,既能保证连接的可靠性,又能提高连接的效率。

4.5.6.1.3 三次握手过程中可以携带数据吗
  • 其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据
  • 为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
  • 也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
4.5.6.1.4 tcp握手时的ACK信息是什么?

TCP握手时的ACK信息是指确认应答,也就是接收端向发送端返回一个已收到数据的通知,并告知下次期望接收的数据包的序列号。例如,如果发送端发送了一个序列号为1000,长度为200的数据包,那么接收端会回复一个确认号为1201的ACK信息给发送端。

4.5.6.1.5 TCP三次握手时,传递的序列号有什么作用?

TCP三次握手时,传递的序列号主要用于同步双方的连接状态。具体来说,连接的建立需要经过三次握手:

  1. 发送方向接收方发送一个SYN请求,并随机生成一个初始序列号(Initial Sequence Number,简称ISN)作为序列号。
  2. 接收方收到SYN请求后,回复一个SYN确认,并生成自己的序列号(即回复序列号)以及确认号(即确认收到的序列号)。同时,也将ISN的值加1作为下一次传输数据的序列号。
  3. 发送方接收到SYN确认后,也发送一个确认,以确认接收到接收方的回复消息,并将确认号设置为接收的回复序列号加1。

通过三次握手,双方就互相确认了对方的状态,建立了连接。传递的序列号被用于确保传输数据的顺序和完整性,以及避免数据包重复或丢失的情况。

4.5.6.2 四次挥手

建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务器均可主动发起挥手动作。

刚开始双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:

  • 第一次挥手: 客户端进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。 客户端 把连接释放的报文段首部的终止控制位FIN置1,其序号seq = u,它等于前面已传送过的数据的最后一个字节的序号加1。这时 客户端 进入FIN-WAIT-1(终止等待1)状态,等待 服务器 的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。

  • 第二次挥手: 服务器 收到连接释放报文段后即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是v,等于 客户端前面已经传送过的数据的最后一个字节的序号加1。然后 服务器就进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应该通知高层应用进程,因而从 客户端 到 服务器这个方向的连接就释放了。这时TCP连接处于半关闭状态,即 客户端已经没有数据要发送了,但服务器若发送数据, 客户端仍要接收。也就是说,从服务器到 客户端这个方向的连接并未关闭,这个状态可能会持续一段时间。客户端收到来自服务器的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待服务器发出的连接释放报文段。

  • 第三次挥手: 若服务器 已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时 服务器 发出的连接释放报文段必须使 FIN = 1。现假定服务器的序号为w,(在半关闭状态服务器可能有发送了一些数据)。服务器必须重复上次已经发送过的确认号 ack = u + 1。这时 服务器 就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

  • 第四次挥手: 客户端在收到b的连接·释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack = w + 1,而自己的序号是seq = u + 1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉,必须经过时间等待计时器(TIME-WAIT timer)设置时间2MSL后,客户端才进入到CLOSED状态。时间MSL叫做最长报文段寿命。

4.6 拥塞控制原理

4.6.1 拥塞

  • 非正式定义:“太多发送主机发送了太多数据或者发送速度太快,以至于网络无法处理”
  • 表现:
    • 分组丢失(路由器缓存溢出)
    • 分组延迟过大(在路由器缓存中排队)
  • 拥塞控制 V.s.流量控制
  • A top-10 problem

4.6.2 拥塞的代价

  • 拥塞时分组延迟太大、达到最大throughput
  • 对给定的“goodput”,要做更多的工作(重传),造成资源的浪费
  • 当分组被drop时,任何用于该分组的“上游”传输能力全都被浪费掉

4.6.3 拥塞的控制

4.6.3.1 端到端的拥塞控制

  • 网络层不需要显示地提供支持
  • 端系统通过观察loss,delay等网络行为判断是否发生拥塞
  • TCP采取这种方法

4.6.3.2 网络辅助的拥塞控制

  • 路由器向发送方显示地反馈网络拥塞信息
  • 简单地拥塞指示(1bit):SNA,DECbit,TCP/IP ECN,ATM)
  • 指示发送方应该采取何种速率

4.6.3.3 网络辅助的拥塞控制案例

  • ABR:available bit rate
    • “弹性服务”
    • 如果发送方路径“underloaded”,使用可用带宽
    • 如果发送方路径拥塞,将发送速率降到最低保障速率
  • RM(resource management cells)
    • 发送方发送
    • 交换机设置RM cell位(网络辅助)
      • NI bit:rate不许增长
      • CI bit:拥塞指示
    • RM cell由接收方返回给发送方

4.7 TCP拥塞控制

4.7.1 基本原理

  • sender限制发送速率

  • CongWin(拥塞窗口)

    • 动态调整以改变发送速率
    • 反映所感知到的网络拥塞
  • 如何感知网络拥塞?

    • Loss事件=timeout或3个重复ACK
    • 发生loss事件后,发送方降低速率
  • 如何合理地调整发送速率?

    • 加性增-乘性减:AIMD

      • 原理:逐渐增加发送速率,谨慎探测可用带宽,直到发生loss

      • 方法:AIMD

        Additive Increase:每个RTT将CongWin增大一个MSS——拥塞避免

        Multiplicative Decrease:发生loss后将CongWin减半

    • 慢启动:SS

      • TCP连接建立时,CongWin=1
      • 可用带宽可能远远高于初始速率:希望快速增长
      • 原理:当连接开始时,指数性增长
      • 指数型增长:
        • 每个RTT将CongWin翻倍
        • 收到每个ACK进行操作
      • 初始速率很慢,但是快速攀升
    • 指数性增长与线性增长切换

      • 变量:Threshold
      • Loss事件发生时,Threshold被设为Loss事件前CongWin值的1/2
  • Loss事件的处理

    • 3个重复ACKs:
      • CongWin切到一半
      • 然后线性增长
    • Timeout事件:
      • CongWin直接设为1个MSS
      • 然后指数增长
      • 达到threshold后,再线性增长

4.7.2 两个TCP拥塞控制算法(Tahoe算法和Reno算法)

  • 刚开始TCP设置cwnd=1.

  • 阶段一:slow start 慢启动阶段,指数增长

    • 第一轮:发出一个包,收到一个ACK后,cwnd+=1.(cwnd =2)
    • 第二轮:发出两个包,如果两个包都回来,则cwnd+=2(cwnd=4)
    • 第三轮:发出四个包,回来后cwnd+=4(cwnd=8)
  • 阶段二:当cwnd达到slow start threshold(ssthresh,慢启动阈值,默认为8)后,进入congestion avoidance 拥塞避免阶段。

    • 回来一个ack,就cwnd+=1/cwnd,即每间隔一个RTT,cwnd增加一个MSS(最大报文长度)
    • 这个阶段理论上没有阈值,但会增长到出口带宽
  • 阶段三:当检测到丢包时(丢包反馈),其中丢包检测的判断为:

    • 发生retransmission timeout(RTO)
    • 连续收到3个重复的ACK(连续收到4个相同的ACK),快速重传

当检测到发生丢包事件时:

Tahoe算法:将ssthresh置为当前cwnd的一半,cwnd置为1,并重新开始进入slow start阶段,如上图的蓝线。

(在TCP Tahoe下,不管是收到三个冗余ACK还是超时检测出丢包,它都会将拥塞窗口的大小减小到1并进入慢启动阶段,cwnd呈指数级增长,达到ssthreth值后呈线性增长。)

Reno算法:进入快速恢复阶段(cwnd不置为1),将ssthresh和cwnd同时置为当前cwnd的一半,并进入拥塞避免阶段,跳过slow start阶段。如上图的黑线。

(Reno算法中,如果是根据三个冗余ACK检测出来的,那么拥塞窗口cwnd的大小应变为当前窗口大小的一半,并在快速恢复阶段加3后再呈线性增长;如果是根据超时检测出来的,那么拥塞窗口cwnd的大小应减小到1,并进入到慢启动状态。)

4.7.3 TCP性能分析

  • TCP throughput:吞吐率
    • 给定拥塞窗口大小和RTT,TCP的平均吞吐率是多少?(忽略掉Slow start)
      • 假定发生超时是CongWin的大小为W,吞吐率是W/RTT
      • 超时后,CongWin=W/2,吞吐率是W/2RTT
      • 平均吞吐率为:0.75W/RTT
  • TCP的公平性
    • 公平性与UDP
      • 多媒体应用通常不使用TCP,以免被拥塞控制机制限制速率
      • 使用UDP:以恒的速率发送,能够容忍丢失
      • 产生了不公平
    • 研究:TCP friendly
    • 公平性与并发TCP连接
      • 某些应用会打开多个并发连接
      • Web浏览器
      • 产生公平性问题
    • 例子:链路速率为R,已有9个连接
      • 新来的应用请求1个TCP,获得R/10的速率
      • 新来的应用请求11个TCP,获得R/2的速率