运输层
为什么需要运输层
运输层(传输层),解决的是计算机程序到计算机程序之间的通信问题,即所谓的“端”到 “端”的通信。
⽹络层最终解决的问题:
分组从⼀台主机经过⽹络到达另⼀台主机,即主机到主机间的通信。
⽹络层没有解决的问题:
1、主机中谁发送的数据、谁接收数据;
2、IP分组⽆序到达⽬的主机,接收进程如何处理;
3、可靠传输问题。
引入传输层的原因:
1、增加复用和分用的功能;
2、消除网络层的不可靠性;
3、提供从源端主机到目的端主机的可靠的、与实际使用的网络无关的信息传输。
进程之间的通信
从 IP 的角度,通信的两端是两台主机。但“两台主机之间的通信”这种说法还不够清楚;严格地讲,两台主机进⾏通信就是两台主机中的应⽤进程互相通信;
从运输层的⻆度看,通信的真正端点并不是主机⽽是主机中的进程。也就是说,端到端的通信是应⽤进程之间的通信。
网络层和运输层的区别
TCP 和 UDP
根据应⽤程序的不同需求,运输层需要有两种不同的运输协议:
1、即⾯向连接的 TCP;
2、⽆连接的 UDP。
这两种协议的差别
TCP 是可靠的,UDP 是不可靠的。
UDP
UDP 的主要特点
1、不需要建⽴连接:减少开销和发送数据之前的时延;
2、尽最⼤努⼒交付:即不保证可靠交付;
3、⾯向报⽂的:⼀次交付⼀个完整的报⽂,保留原始报⽂的边界;
-
发送方的 UDP 程序对应用程序交下来的报文,添加首部后,就向下交付 IP 层。UDP 对应应用层交下来的报文,即不合并也不拆分,而是保留这些报文的边界。也就是说,应用层交给 UDP 多长的报文,UDP 就照样发,即一次发送一个报文。
-
接收方的 UDP ,对于 IP 层交上来的 UDP 用户数据报,在去除首部后就原封不动的交付给上层的应用进程。也就是说 UDP 一次交付一个完整的报文。
4、没有拥塞控制:⽹络出现拥塞不会降低源主机的发送速率;对某些实时应⽤是很重要的。
5、⽀持多种交互通信: • ⽀持⼀对⼀、⼀对多、多对⼀和多对多的交互通信。
6、⾸部开销⼩:8 个字节,⽐ TCP 的 20 个字节的⾸部要短。
UDP 多对一
UDP 的使用场景
可以重复请求信息的情况下:
- 例如:RIP,DNS,DHCP等;
⼀次性传⼩量数据的应⽤(⾯向报⽂的)
-
实时应⽤: • IP电话、视频会议等;
-
多媒体应⽤。
TCP
1、TCP 是面向连接的运输层协议。也就是说,应用程序在使用 TCP 之前,必须先建立 TCP 连接,在传送送数据完成时,必须释放已经建立的连接。
2、每一条 TCP 连接只能有连个端点,TCP 连接是点对点的(⼀对⼀);
3、TCP 提供可靠的交付服务。通过 TCP 连接传送的数据,无差错,不丢失不重复,并且按时到达。
4、TCP 提供全双工通信。TCP 允许通信的双方的应用进程在任何时候都能发送数据;
5、面向字节流。TCP 中的流值得是流入进程和从进程流出的字节序列。
-
面向字节流的含义:虽然应用程序和 TCP 的交互是一次一个数据块,但 TCP 把应⽤程序交下来的数据看成仅仅是⼀连串⽆结构的字节流。
-
TCP 并不知道所传送的字节的流的含义,TCP 不保证接收方收到的数据块和发送方应用程序所发出的数据块大小具有对应大小的关系。
-
但是接收方应用程序收到的字节和发送方应用程序发出的字节流完全一样。
接收方应用程序应该有能力识别收到的字节流,并把它还原成有意义的应用层数据。
TCP 的连接
TCP 把连接作为最基本的抽象:每⼀条 TCP 连接有两个端点,TCP 连接的端点不是主机,不是主机的IP 地址,不是应⽤进程,也不是运输层的协议端⼝,TCP 连接的端点叫做套接字 (socket) 或插⼝,端⼝号拼接到 (contatenated with
) IP 地址即构成了套接字。
套接字 socket = (IP地址 : 端⼝号)
TCP 连接 ::= {socket1, socket2} = {(IP1: port1),(IP2: port2)}
同一个 IP 地址可以有多个不同的 TCP 连接;
同一个端口号也可以出现在多个不同的 TCP 连接中。
可靠性传输原理
TCP 发送的报文段是交给 IP 层传送的,但是 IP 只能提供尽最大努力交付,简单的说就是 TCP 下面的网络层提供的是不可靠的传输。
理想的传输条件有下面两个特点
1、传输信道不产生差错;
2、不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
但是实际的网络层都不具有上面的两个理想条件,必须使用一些可靠的传输协议,在不可靠的传输信道中实现可靠传输。
停止等待协议
停止等待:就是每发送完一个分组就停止发送,等待对方的确定。在收到确认后再发送下一个分组。
无差错情况
无差错的情况下是最简单的,只需要在每组数据发送完成之后,收到确认继续发送就行了。
有差错情况
接收方 B 会出现下面两种错误
1、B 接收到 M1 检测出了差错,就丢弃 M1,其他什么也不做;
2、M1 在传送的过程中丢失了,这时候 B 就获取不到,也什么也不用做;
如果解决上面的两种情况呢?
答案:超时重传。
可靠传输协议是这样设计的,只要 A 超过了一段时候仍然没有收到确认,就认为刚刚发送的分组丢失了,因而重传前面发送过的分组。
不过下面三个点需要注意下
1、A 在发送完一个分组之后,必须暂时保留已发送的分组副本(在发生超时重传时使用),只有收到了响应的确认,才能清除暂时保留的副本。
2、分组和确认分组都必须进⾏编号。这样才能明确是哪⼀个发送出去的分组收到了确认,⽽哪⼀个分组还没有收到确认;
3、超时计时器设置的重传时间应当⽐数据在分组传输的平均往返时间更⻓⼀些。太长,通信的效率就会很低,太短了会导致没有必要的重传。
确认丢失和确认迟到
确认丢失
A 在超时计时器到期后重传 M1,如果 B ⼜收到了重传的分组 M1:
1、丢弃这个重复的分组 M1;
2、向 A 发送确认。
出现差错
B 对分组 M1 的确认迟到了:
1、B 丢弃重复的 M1,并重传确认分组;
2、A 会收到重复的确认:收下后就丢弃。
总结下:
通常 A 最终总是可以收到对所有发出的分组的确认;
如果 A 不断重传分组但总是收不到确认,就说明通信线路太差,不能进⾏通信;
使⽤上述的确认和重传机制,我们就可以在不可靠的传输⽹络上,实现可靠的通信;
上述协议称为自动重传请求 ARQ,意思就是重传的请求是自动进行的,接收方不需要请求发送方重传某个出错的分组。
信道利用率
停止等待协议的优点是简单,缺点也很明显,信道的利用率太低了。
来总结下停止等待协议的要点
-
停⽌等待。发送⽅每次只发送⼀个分组。在收到确认后再发送下⼀个分组;
-
编号。对发送的每个分组和确认都进⾏编号;
-
⾃动重传请求。发送⽅为每个发送的分组设置⼀个超时计时器。若超时计时器超时,发送⽅会⾃动重传分组;
-
简单,但信道利⽤率太低。
为了提高传输效率,可以不使低效率的停止等待协议,而是采用流水线传输。
流水线传输,就是发送方可以连续发送多个分组,不必每次发送完成就停顿下来等待对方的确认。这样可以使信道上一直有数据不断在传送,这种方式可以提高信道的利用率。
当使⽤流⽔线传输时,连续ARQ协议和滑动窗⼝协议:限制连续发送数据的量。
连续 ARQ 协议
连续 ARQ 协议规定,发送方每收到一个确认,就把窗口向前活动一个分组位置。
接收方一般采用累计确认的方式。就是接收方不必对收到的分组进行逐一确认,而是在收到几个分组之后,对按序到达的最后一个分组发送确认。表示这个分组为止的所有分组都已经正确收到了。
优点:容易实现,即使确认丢失也不必重传。
缺点:不能向发送方反应出接收方已经正确收到所有分组信息。
Go-back-N(回退 N)
发送⽅发送了前 5 个分组,第 3 个分组丢失,接收⽅对收到1、2分组发出确认,发送⽅⽆法知道3、4、5的下落,只好把这三个分组再重传⼀次。
这就叫做 Go-back-N(回退 N)
,表示需要再退回来重传已经发送的 N 个分组。所以当通信线路质量不好时,连续 ARQ 协议会带来负面的影响。
滑动窗口的具体细节,看下文
TCP 报文段的首部格式
TCP 虽然是面向字节流的,但是 TCP 的传送单元式报文段。
一个 TCP 分成首部和数据两部分,TCP 的全部功能体现在首部的各字段的作用。所以有必要弄清除这几个字段的作用。
TCP 报⽂段⾸部的前20个字节是固定的,后⾯有 4n 字节是根据需要⽽增加的选项。
1、源端口和目的端口:各占 2 个字节,分别写入源端口号和目的端口号,是运输层与应用层的服务接口;
2、序号:4 字节,也称报文段序号,指报文段所携带数据的第一个字节的序号;
-
TCP 是面向字节流的,在一个 TCP 连接中传送的字节流的每一个字节都按序号进行编号,整个要传送的字节流的起始序号必须在连接建立时设置。
-
栗子
-
一报文段的序号字段值是 301,携带的数据是 100 字节。这表示本报文段,第一个字节的序号是 301, 最后一个字节的序号是 400 。
-
下一个报文段的数据序号应该从 401 开始。
3、确认号:4 个字节,是期望收到对方下一个报文段的第一个字节的序号。
- 这里有个规律,若确认号是 N,就表明到序号 N -1 之前的数据都已正确收到了。
4、数据偏移:占 4 位,它指出 TCP 报⽂段的数据起始处距离 TCP 报⽂段的起始处有多远,实际上指的是 TCP 报文段的首部长度;
5、保留:占 6 位,保留为今后使用,目前应该置为 0;
6、紧急 URG :当 URG = 1 时,紧急指针字段有效,此报⽂段中有紧急数据,应尽快传送;
- 栗如:当我们想要结束程序的执行使用中断命令(control + c),如果不使用紧急数据,那么这个命令就会存储在接收 TCP 的缓存末尾,只有等待前面的所有数据被处理完成之后,这两个字符才会被交付给接收方的应用程序、
7、确认 ACK:只有当 ACK = 1 时确认号字段才有效。当 ACK = 0 时,确认号⽆效, TCP 规定在连接后所有传送的报文都必须把 ACK 置为 1;
8、推送 PSH(PuSH):收到 PSH = 1 的报⽂段,发送⽅⽴即发送,接收⽅尽快上交接收应⽤进程;
9、当 RST = 1 时,表明 TCP 连接中出现严重差错,必须释放连接,然后再重新建⽴运输连接;
10、同步 SYN: SYN = 1 表示这是一个连接请求或连接接受报文;
11、终止 FIN: 用来释放一个链接,FIN = 1,表示此报文段的发送端的数据已经发送完毕,并要求释放运输连接;
12、窗口:占 2 个字节,让对方设置发送窗口的依据,单位为字节,[ 0, 216 – 1 ]
之间的整数,主要作用是告诉对⽅,从本报⽂段⾸部中的确认号算起,接收⽅⽬前允许对⽅发送的数据量;
- 为什么需要这个限制呢,是因为数据缓存空间是有限的,窗口值,可以作为接收方设置发送串口的依据。
13、校验和:占 2 个字节,检验和字段检验的范围包括⾸部和数据这两部分。在计算检验和时,要在 TCP 报⽂段的前⾯加上 12 字节的伪⾸部;
14、紧急指针:占 2 个字节,紧急指针仅在 URG = 1 时才有意义,它指出本报文段中紧急数据的字节数,(紧急数据结束后就是普通数据);
15、选项:长度可变,TCP 最初只有⼀种选项,即最⼤报⽂段⻓度 MSS。MSS 告诉对⽅ TCP:“我的缓存所能接收的报⽂段的数据字段的最⼤⻓度是 MSS 个字节。”
TCP 可靠传输的实现
TCP 连接中的连个端点都有两个窗口:
发送窗口:准备发送的数据和已经发送但未收到确认的数据;
接收窗口:按序到达但未被程序接收的数据、不按序到达的数据;
TCP 两端的四个窗口经常处于动态变化中。
TCP 的可靠传输是用字节的序号进行控制。TCP 中所有的确认都是基于序号而不是基于报文段的。
TCP连接的往返时间 RTT 不是固定不变的,需要使⽤特定的算法估算较为合理的重传时间。
来看下具体滑动窗口的实现
1、发送方根据确认号和窗口值的大小,就能确定自己的发送窗口;
2、发送放可以把落入滑动窗口中的序号字节一次性全部发送出去,边发送边接收;
3、如果数据全部发送完成,但是没有收到确认,那么就需要停止发送,这时候的有效窗口就是0;
4、发送窗口中,收到了确认号,那么就根据当前最新的确认号和窗口大小来调整活动窗口,将新的字节划入到滑动窗口中;
- 这里有个规律,若确认号是 N,就表明到序号 N -1 之前的数据都已正确收到了。
5、接收窗口在收到数据后,会对按序收到的数据中的最高序号给出确认,这样就保证发送窗口中,收到的确认号前面的数据都已经被接收窗口收到了;
6、如果发送端经过一段时候没有收到确认,就会使用超时控制器,来重传这一部分的数据,重新设置超时间,直到收到接收端的确认。
TCP 流量控制
为什么需要流量控制,我们总是希望数据传输能快一点,但是如果发送方,发送过快,接收方来不及处理,这就会造成数据的丢失。
流量控制就是让发送方的发送频率不要太快,接收方来得及接收处理。
可以使用滑动窗口来实现流量控制,通过接收方的接收能力来限制发送方的发送能力。
在通信的过程中,接收方会根据自己的接收缓存的大小,通过 rwnd 动态的告知发送窗口自己接收窗口的大小,发送放的发送窗口不能超过接收方给出的接收窗口的数值。这样就实现了流量控制。
死锁
不过还会出现一种错误的情况,B 向 A 发送了零窗口的报文段之后,B 的接收缓冲区又有了一些存储空间。于是 B 向 A 发送了 rwnd=400
的报文段。但是这个报文段在传输的过程中丢失了。
那么 A 就会一直等待 B 发送的 rwnd 窗口的通知,同时 B 也一直等待 A 发送的数据。如果没有措施,那么就会处于死锁的状态一直持续。
如何解决
TCP 为每一个链接设置一个持续计时器,只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文。
对方收到这个确认报文,就会回复现在的窗口值,如果窗口值仍然是零,那么收到这个报文段的一方就重新设置持续计时器,如果窗口不是零,那么死锁的僵局就可以打破。
TCP 的传输效率
应用进程把数据发送到 TCP 的发送缓存后,剩下的发送任务就由 TCP 来控制了,可以使用不同的机制来控制 TCP 报文段的发送时机。
1、第一种机制:TCP 维持⼀个变量,它等于最⼤报⽂段⻓度 MSS。只要缓存中存放的数据达到 MSS 字节时,就组装成⼀个 TCP 报⽂段发送出去;
2、第二种机制:由发送放的应用进程指明要求发送报文段,即 TCP 支持的推送(push)操作,紧急数据 URG;
3、第三种机制:发送方的一个计时器期限到了,就把当前已有的缓存数据转入到报文段(但长度不超过 MSS)发出去。
举个栗子
一个交互式用户使用一条 TELENT 连接,假设用户只发送一个字符。
用户发送一个字符,加上 20 字节的首部后,得到了 21 字节长的 TCP 报文段,在加上 20 字节的 IP 首部,形成 41 字节长的 IP 数据报;
接收方 TCP 立即发出确认,构成的数据报是 40 字节长(假定没有数据发送);
若用户要求远程主机返回这一字符,则又要发回 41 字节长的 IP 数据报和 40 字节长的确认 IP 数据报。
当线路资源并不富裕的时候,这种传送方式的效率并不高。
对于这种情况,有下面两种方式处理
Nagle 算法
Nagle 算法主要是避免发送小的数据包,要求 TCP 连接上最多只能有一个未被确认的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP 收集这些少量的小分组,并在确认到来时以一个分组的方式发出去。
具体的算法过程
发送⽅先发送第⼀个数据字节,缓存后⾯到达的数据字节;
发送⽅收到对第⼀个数据字符的确认后,把发送缓存中的所有数据组装成⼀个报⽂段发送出去,继续对随后到达的数据进⾏缓存;
只有在收到对前⼀个报⽂段的确认后继续发送下⼀个报⽂段;
当到达的数据已达到发送窗⼝⼤⼩的⼀半或已达到报⽂段的最⼤⻓度时,就⽴即发送⼀个报⽂段。
糊涂窗口综合症
TCP 通过滑动窗口来进行流量控制,当接收方发现自己跟不上发送的速度了,就缩小接收窗口大小,抑制发送方的发送速度,防止发送方发送太快。
如果发送方的接收能力越来越差怎么办?
举个栗子
接收方的 TCP 缓存区已满,接收方会向发送方发送窗口大小为 0 的报文;
接收方的应用进程每次只读取⼀个字节,接收⽅发送窗⼝⼤⼩为⼀个字节的确认报⽂,发送⽅发送⼀个字节的数据,接收窗⼝⼜满了,如此循环往复;
简单讲就是,糊涂窗口综合症就是接收方窗口变小,导致发送方每次发送的数据只有一个大大的头部,真正携带的数据很少。
处理策略
接收方
可以让接收方等待一段时间:
1、使得接收缓冲区已有足够的空间容纳一个最长的报文;
2、或者等待缓冲区有一半的空闲空间。
接收方满足上面两个个条件中的任意一个就可以发出确认报文,向发送放发送当前的窗口大小。
发送方
发送方也不要发送太小的报文段,把数据报积累成组足够大的报文段,或者达到接收方缓冲区一半大小。
其实就是上文的 Nagle 算法。
上面两种方法结合使用,发送方不要发送过小的报文段,同时接收方不要有一点空间就急忙通知发送方。
TCP 拥塞控制
在某段时间内,若网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏,这种现象成为阻塞。
如果网络中的许多资源同时产生阻塞,网络的性能就要明显变坏,整个网络的负荷将随输入负荷的增大而下降。
阻塞的一般原因:
1、链路容量不⾜、资源分配不均衡;
2、路由器缓存空间、流量分布不均衡;
3、处理机速度太慢。
增加资源能解决阻塞吗?
答案是不能,增加资源可能还会加剧阻塞的情况。
栗如
1、增⼤缓存,但未提⾼输出链路的容量和处理机的速度,排队等待时间将会⼤⼤增加,引起⼤量超时重传,解决不了⽹络拥塞;
2、提⾼处理机处理的速率会将瓶颈转移到其他地⽅。
阻塞常常趋于恶化
路由器没有⾜够的缓存空间,它就会丢弃⼀些新到的分组,分组被丢弃时,发送这⼀分组的源点就会重传这⼀分组,甚⾄可能还要重传多次。这样会引起更多的分组流⼊⽹络和被⽹络中的路由器丢弃。拥塞引起的重传不会缓解⽹络的拥塞,反⽽会加剧⽹络的拥塞。
拥塞控制:防止过多的数据注入到网络中,使链路中的路由器和链路不至于过载。
拥塞控制的前提:⽹络能够承受现有的⽹络负荷。
拥塞控制是⼀个全局性的过程:涉及到所有的主机、所有的路由器,以及与降低⽹络传输性能有关的所有因素。
拥塞控制与流量控制的区别
流量控制:是端到端的问题(接收端控制发送端),点对点的通信流量控制。抑制发送端发送频率,以便接收端来得及接收。
拥塞控制:是一个全局性的控制,涉及到与降低网络传输性能有关的所有因素。防止过多数据注入到网络,使网络中的路由器或链路不至于过载。
某些拥塞控制算法是向发送端发送控制报⽂,并告诉发送端,⽹络已出现麻烦,必须放慢发送速率,与流量控制是很相似。
阻塞控制的一般原理
由于阻塞是一个动态的问题,阻塞控制比较难设计
分组的丢失是⽹络发⽣拥塞的征兆⽽不是原因:
-
数据链路层:帧出错被丢弃;
-
⽹络层:出错IP分组被丢弃;
-
拥塞控制本身也可能成为⽹络性能恶化甚⾄发⽣死锁的原因。
开环控制⽅法:在设计⽹络时考虑发⽣拥塞的因素,⼒求⽹络不产⽣拥塞。
闭环控制⽅法,基于反馈环路的概念:
-
监测⽹络系统以便检测到拥塞在何时、何处发⽣;
-
将拥塞发⽣的信息传送到可采取⾏动的地⽅;
-
调整⽹络系统的运⾏以解决出现的问题。
阻塞通知的传递与时机
阻塞控制的时机选择
-
过于频繁,会使系统产生不稳定的振荡;
-
过于迟缓,采取行动又不具有任何价值;
采取的策略
-
发送“通知拥塞发⽣”的分组;
-
在分组中保留表示拥塞状态的字段;
-
周期性地发出探测分组等。
TCP 阻塞控制算法
TCP 进行阻塞控制的算法有四种:
1、慢开始 (slow-start);
2、拥塞避免 (congestion avoidance);
3、快重传 (fast retransmit);
4、快恢复 (fast recovery)。
TCP 采用基于窗口的方法进拥塞控制,该方法属于闭环控制方法
TCP发送⽅维持⼀个拥塞窗⼝ CWND (Congestion Window):
拥塞窗⼝的⼤⼩取决于⽹络的拥塞程度,并且动态地在变化;
1、发送端利⽤拥塞窗⼝根据⽹络的拥塞情况调整发送的数据量;
2、⽹络没有出现拥塞,拥塞窗⼝增⼤⼀些,以便发送更多的分组,提⾼⽹络的利⽤率;
3、⽹络出现拥塞或有可能出现拥塞,拥塞窗⼝减⼩⼀些,减少注⼊到⽹络中的分组数。
真正的发送窗⼝值 = Min(公告窗⼝值,拥塞窗⼝值)
TCP 的拥塞的判断依据
重传定时器超时,现在通信线路的传输质量⼀般都很好,因传输出差错⽽丢弃分组的概率很⼩(远⼩于 1 %)。只要出现了超时,就可以猜想⽹络可能出现了拥塞。
慢开始
目的:用来确定网络的负载能力或拥塞程度。
算法思路:
当主机开始发送数据的时候,由于不清除网络的符合情况,如果把大量的数据字节注入到网络,那么就有可能引起网络的阻塞,经验证明,较好的方法是先探测一下,然后由小到大增加发送窗口。
发送最大报文段 SMSS: Sender Maximum Segment Size(MTU、MSS)
初始拥塞窗⼝ cwnd 设置(限制初始拥塞窗⼝的字节数):
旧的规定: 1 ⾄ 2 个最⼤报⽂段
RFC 5681 规定: 2 ⾄ 4 个最⼤报⽂段
-
若
SMSS > 2190
字节,则设置初始阻塞窗口cwnd = 2 × SMSS
字节,且不得超过2个报⽂段; -
若
SMSS > 1095
字节且SMSS ≤ 2190
字节,则设置初始阻塞窗口cwnd = 3 × SMSS
字节,且不得超过3个报⽂段; -
若
SMSS ≤ 1095
字节,则设置初始阻塞窗口cwnd = 4 × SMSS
字节,且不得超过4个报⽂段;
在每收到⼀个对新的报⽂段的确认后,把拥塞窗⼝增加最多⼀个 SMSS 的数值。
拥塞窗⼝ cwnd 每次的增加量 = min (N, SMSS)
其中 N 是原来未被确认,但现在刚收到的确认报文段所确认的字节数。
当 N < SMSS
时,拥塞窗⼝每次的增加量⼩于 SMSS,采⽤逐步增⼤发送⽅的拥塞窗⼝的⽅法,注⼊数据⾄⽹络的速率更加合理。
为方便计算,窗口大小采用报文段个数计算
可以看到每经过一个传输轮次,阻塞窗口 cwnd 就加倍。
同时为了防止阻塞窗口 cwnd 增长过快引起网络阻塞,还需要设置一个慢开始门限 ssthresh 状态变量。
-
当
cwnd < ssthresh
时,使⽤慢开始算法; -
当
cwnd > ssthresh
时,停⽌使⽤慢开始算法⽽改⽤拥塞避免算法; -
当
cwnd = ssthresh
时,既可使⽤慢开始算法,也可使⽤拥塞避免算法。
避免阻塞算法的思路:让拥塞窗口 cwnd 缓慢的增大,每经过一个往返时间 RTT 就把发送方的窗口 cwnd 加一,而不像慢开始阶段那样加倍增长。因此在阻塞阶段就有加法增大
的特点。这表明在拥塞避免阶段,拥塞窗口 cwnd 按线性规律缓慢增大,比慢开始算法的拥塞窗口增长速率缓慢很多。
1、TCP 连接初始化,拥塞窗口 cwnd 设置成 1,慢开始门限的初始值 ssthresh = 16
;
2、在执行慢开始算法的时候,发送方每收到一个对新报文段的确认,就把阻塞窗口加 1,然后开始下一轮的传输,阻塞窗口随着传输轮次指数规律增长;
3、当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时,就改为执行拥塞避免算法,拥塞窗口按照线性增长,图中的 1~2 阶段;
需要注意的是:拥塞避免并非完全能够避免拥塞,拥塞避免是把拥塞窗口控制为按照线性规律增长,使网络不易出现拥塞。
4、当拥塞窗口为 24 时,网络出现了超时,图中的 2 点,发送方判断网络阻塞,于是调整了门限值 ssthresh = cwnd/2 = 12
,同时设置拥塞窗口 cwnd = 1,进入慢开始阶段,然后进入拥塞避免阶段;
5、节点 4 可以看到发送方收到了 3 个对同一报文段的确认;
因为个别报文段会在传输中丢失,但是网络其实没有发生阻塞,如果发送方迟迟收不到确认,就会误认为网络发生了阻塞,然后就会错误的启动慢开始,降低了传输的效率。
这时候就需要用到快重传算法了
快重传算法可以让让发送方尽早知道发生了个别报文段的丢失。快重传算法要求接收方不要等待自己发送数据是才携带确认,而是立马要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。
发送方只要一连收到了三个重复确认,应当立即进行重传(即快重传),这样就不会出现超时,发送放不会误认为出现了网络阻塞。
使用快重传可以使整体的网络吞吐量提高约 20%
快恢复算法
当发送端收到连续三个重复的确认时,发送⽅认为⽹络很可能没有发⽣拥塞,因此不执⾏慢开始算法,⽽是执⾏快恢复算法FR (Fast Recovery) 算法:
-
慢开始⻔限 ssthresh = 当前拥塞窗⼝ cwnd / 2 ;
-
新拥塞窗⼝ cwnd = 慢开始⻔限 ssthresh ;
-
开始执⾏拥塞避免算法,使拥塞窗⼝缓慢地线性增⼤。
主动队列管理 AQM
网络层的策略对于 TCP 拥塞控制影响最大的就是路由器的分组丢弃策略,在最简单的情况下,路由器的队列通常是按照先进先出
的规则来处理到来的分组。由于队列的长度是有限的,当队列已经满了的时候,后面到达的分组将会被丢弃,这既是尾部丢弃策略
。
路由器的尾部丢失会、导致一连串分组的丢失,进而导致发送方出现超时重传。使 TCP 进入到拥塞控制中的慢开始状态,结果使 TCP 连接的发送方,突然把数据的发送频率降低到最小数值。
网络中通常有多条 TCP 连接,若发生了尾部丢失,会影响到多条 TCP 连接,结果是多条 TCP 连接在同一时刻都进入到慢开始状态,这在 TCP 中的术语称为全局同步。
为了避免网络中的全局同步现象,1998 年提出了主动队列管理 AQM。
不要等到路由器的队列⻓度已经达到最⼤值时才不得不丢弃后⾯到达的分组,在队列⻓度达到某个值得警惕的数值时(即当⽹络拥塞有了某些拥塞征兆时),就主动丢弃到达的分组。
AQM 有不同实现⽅法,曾流⾏多年的是随机早期检测 RED (Random Early Detection)
。
RED 的原理
实现 RED 需要维持两个参数,队列长度最大门限和最小门限。当每一个分组到达的时候,RED 就会按照规定的算法计算出当前队列的平均队列长度。
1、若平均队列长度小于最小的门限,则把新到达的分组放入到队列中进行排序;
2、若平均队列长度超过最大门限,则把新到达的分组丢弃;
3、若平均队列长度在最小门限和最大门限之间,则按照一定的概率 P 把新到达的分组丢弃(这里体现了丢弃分组的随机性);
RED 不是等到已经发生网络阻塞的时候,才把所有在队列尾部的分组丢弃,而是检测到了网络阻塞的早期症状的时候,就以概率 P 丢弃个别分组,让阻塞只在个别 TCP 连接中进行,避免发生全局性的拥塞控制。
TCP 的连接运输管理
TCP 是面向连接的协议,运输连接是用来传送 TCP 连接的。
运输连接有三个阶段:
1、连接建立;
2、数据传输;
3、连接释放。
TCP 连接需要解决下面三个问题
1、要使每一方都能确知对方的存在;
2、要允许双方协商一些参数((如最⼤窗⼝值、是否使⽤窗⼝扩⼤选项和时间戳选项以及服务质量等);
3、能够对运输实体资源(如缓存⼤⼩、连接表中的项⽬等)进行分配.
TCP 连接采用的是客户服务器方式
主动发起连接建⽴的应⽤进程叫做客户(client);
被动等待连接建⽴的应⽤进程叫做服务器(server)。
TCP 连接的建立
TCP 建立连接的过程叫做握手,握手需要在客户端和服务端之间交换三个 TCP 报文段。
来看下具体的握手细节
第一个报文:A 的 TCP 向 B 发出连接请求报⽂段:其⾸部中的SYN = 1,seq = x
,表明传送数据时的第⼀个数据字节的序号是 x;
- SYN = 1 表示这是一个连接请求或连接接受报文;
第二个报文:B 收到连接请求报⽂段后,如同意,发回确认。确认报⽂段中 SYN = 1, ACK = 1
,其确认号ack = x + 1,⾃⼰的序号 seq = y。
- 确认 ACK:只有当 ACK = 1 时确认号字段才有效。当 ACK = 0 时,确认号⽆效, TCP 规定在连接后所有传送的报文都必须把 ACK 置为 1;
第三个报文:A 收到同意报⽂后,向 B 给出确认,ACK = 1,确认号 ack = y + 1。A 的 TCP 通知上层注意SYN报⽂需消耗⼀个序号 应⽤进程,连接已经建⽴。
为什么需要三报文握手建立连接
进行三报文握手建立连接主要是防止已经失效的连接请求报文段忽然又传送到了 B 进而产生错误。
举个异常的栗子:
1、A 发出一个连接请求,但是由于某些原因没有收到确认,然后 A 就重新发起了一个新的连接请求;
2、后发起的连接和 B 进行连接确认,建立了连接,数据传输完成,就释放了连接,本次请求结束;
3、但是如果 A 发出去第一个连接请求报文段没有丢失,而是在某个网络节点滞留了,当 B 收到这个连接请求报文段之后,就认为 A 又重新发起了一次新的连接请求;
4、如果不采用报文握手,只要 B 发出确认,新的连接就建立了;
由于现在 A 没有发出建立连接的请求,因此不会理会 B 的确认,也不会向 B 发送数据。但是 B 却以为新的连接已经建立了,并且一直等待 A 数据的发送,这样就会造成 B 的许多资源被白白浪费了。
采用三报文握手的方式就能避免这种情况,对于上面的异常情况,A 不会向 B 发出确认,B 没有收到确认,就知道 A 没有跟它建立连接。
采用三报文握手建立 TCP 连接的各状态
-
CLOSED 关闭状态;
-
LISTEN 收听状态;
-
SYN-SENT 同步已发送状态;
-
SYN-RCVD 同步收到状态;
-
ESTAB-LISHED 建立连接状态;
TCP的连接释放
四次报文挥手释放连接
通信双方结束后,都可以释放连接。A B 双方都处于 ESTAB-LISHED 状态;
1、A 的应用进程向 B 发出连接释放报文段,并停止发送数据,主动关闭 TCP 连接,A 释放报文传递 FINN=1,其序号 seq = u,它等于前面已经传过的数据的最后一个字节加 1,此时 A 进入到 FIN-WAIT-1(终止等待1)状态,等待 B 的确定;
- 终止 FIN: 用来释放一个链接,FIN = 1,表示此报文段的发送端的数据已经发送完毕,并要求释放运输连接;
2、B 收到连接释放报文后就立即发出确认,然后 B 进入到 CLOSE-WAIT(关闭等待)状态,这时候从 A 到 B 的连接就释放了,这时候的 TCP 处于半关闭的状态;
- A 到 B 方向的连接已经关闭了,但是 B 到 A 连接还没有关闭,这时候 B 还可以向 A 发送数据。
3、A 收到 B 的确认就进入到 FIN-WAIT-2 (终止等待2),等待 B 发出的连接释放报文;
4、上面三个步骤完成了 A 到 B 的连接释放过程。下面看下 B 到 A 的连接释放过程;
5、如果 B 已经没有数据发送给 A 了,就开始连接的释放过程,B 发出连接释放报文,FIN = 1,发送 B 当前的序号 seq = w,同时重复已经发送过的确认号 ack = u + 1
,这时候 B 进入到 LAST-ACK 最后确认状态,等待 A 的确认;
6、A 收到 B 的连接释放报文之后,就对此发出确认,这时候 A 就进入到 TIME-WAIT 等待状态,B 收到确认报文就进入到 CLOSED 状态;
- 不过这时候的 TCP 连接还没有释放掉,必须经过时间等待计时器设置的时间 2MSL 之后,A 才能进入到 CLOSED 状态,MSL 叫做最长报文段寿命。
A 为什么需要等待 2MSL
1、保证TCP协议的全双⼯连接能够可靠关闭;
为了保证 A 发送的最后一个 ACK 报文能够到达 B,如果 Client 直接进⼊ CLOSED 状态,Server 有可能没有收到 Client 最后回复的 ACK,Server 超时之后继续发送 FIN。但 Client 已经进⼊ CLOSED 状态了,服务器重发的 FIN 找不到对应的连接,Server 就无法按照正常步骤进入到 CLOSED 状态;
2、保证这次连接的重复数据从网络中丢失;
假设 Client 在进入到 CLOSED 之后,又向 server 端发起了新的请求,有可能新的连接和老的连接的端口号是相同的;
但是 Client 端在关闭之前的一些数据滞留在网络中,这些数据在建立新的连接之后才到达 server ,TCP 协议认为这些数据属于新连接的,那么就会和真正的新连接的数据包发生混淆;
Client 在 TIME_WAIT 状态等待 2 倍 MSL,这样可以保证本次连接的所有数据都从⽹络中消失(Server 与 Client 的连接已经释放,这期间 Server 收到的 TCP 报⽂段,直接丢弃)。
保活计时器
用来防止 TCP 连接出现长时期的空闲。
保活计时器 通常设置为2⼩时 。若服务器过了2⼩时还没有收到客户的信息,它就发送探测报⽂段。若发送了10个探测报⽂段(每⼀个相隔75秒)还没有响应,就假定客户出了故障,因⽽就终⽌该连接。
总结
1、运输层提供应用进程的逻辑通信,也就是说,运输层之前的通信并不是真正的在两个运输层之间直接传送数据,运输层向应用层屏蔽了下面网络的细节,它使应用进程看见的就好像在两个运输实体之间有一条端到端的逻辑通信信道;
2、网络层为主机之间提供逻辑通信,而运输层为应用进程之前提供端到端的逻辑通信;
3、根据应⽤程序的不同需求,运输层需要有两种不同的运输协议:
1、即⾯向连接的 TCP;
2、⽆连接的 UDP。
这两种协议的差别
TCP 是可靠的,UDP 是不可靠的。
4、运输层用一个 16 位的端口号来标识一个端口,端口号只具本地意义;
5、停止等待:就是每发送完一个分组就停止发送,等待对方的确定。在收到确认后再发送下一个分组;
6、超时重传,只要超过了一段时候仍然没有收到确认,就认为刚刚发送的分组丢失了,因而重传前面发送过的分组;
7、连续 ARQ 协议规定,发送方每收到一个确认,就把窗口向前活动一个分组位置;
接收方一般采用累计确认的方式。就是接收方不必对收到的分组进行逐一确认,而是在收到几个分组之后,对按序到达的最后一个分组发送确认。表示这个分组为止的所有分组都已经正确收到了。
8、TCP 中使用滑动窗口来实现可靠传输;
9、为什么需要流量控制,我们总是希望数据传输能快一点,但是如果发送方,发送过快,接收方来不及处理,这就会造成数据的丢失;
10、在某段时间内,若网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏,这种现象成为阻塞。如果网络中的许多资源同时产生阻塞,网络的性能就要明显变坏,整个网络的负荷将随输入负荷的增大而下降;
11、为什么需要三报文握手,进行三报文握手建立连接主要是防止已经失效的连接请求报文段忽然又传送到了 B 进而产生错误;
12、客户端为什么需要等待 2MSL;
1、保证TCP协议的全双⼯连接能够可靠关闭;
2、保证这次连接的重复数据从网络中丢失;
参考
【极客时间-趣谈网络协议】https://time.geekbang.org/column/intro/100007101
【计算机网络第八版】https://www.bilibili.com/video/BV1WP4y1j7JU?p=1
【计算机网络学习笔记】https://github.com/boilingfrog/Go-POINT/tree/master/tcp