TCP协议有哪些知识点?

265 阅读15分钟

TCP是面向连接、面向字节流的。

一次数据传输要经过三个阶段

  • TCP三报文握手
  • 传输数据(TCP在其中默默地进行流量控制和拥塞控制)
  • TCP四报文挥手

一、TCP三次握手的过程/TCP的四报文挥手

1、三次握手

① 三次握手过程

  • 第一次握手:客户端发送一个TCP连接请求,SYN=1,seq=x,SYN=1表明给TCP连接请求不能携带数据.发送后进入同步已发送状态.

  • 第二次握手:服务端接收到请求,进入同步已接收状态,发回一个TCP确认报文段,SYN=1,seq=y,ACK=1,ack=x+1

  • 第三次握手:客户端收到了TCP连接请求确认报文段之后,要向TCP服务进程发送一个普通的TCP确认报文段,ACK=1,seq=x+1,ack=y+1,进入连接已建立状态。由于没有SYN=1,则此普通的TCP确认报文段可携带数据。服务端接收到客户端发来的普通的TCP确认报文段,也进入连接已建立状态

② 为什么采用三次握手而不是两次?

三次握手的目的在于防止网络中已经失效的连接请求突然又被发送到了服务器,占用TCP服务端的资源

2、四次挥手

① 四次挥手过程

  • 第一次挥手:TCP客户端发送TCP连接释放报文段,FIN=1 ACK=1 seq=u ack=v,进入终止等待1状态

  • 第二次挥手:TCP服务端发回一个普通的TCP确认报文段,ACK=1,seq=v,ack=u+1,进入关闭等待状态.TCP服务器进程此时还要通知高层服务器进程,客户端要断开TCP连接。此时TCP客户端进程到TCP服务器进程这个方向的连接就释放掉了,TCP连接处于半关闭状态。TCP客户进程收到TCP确认报文段后就进入终止等待2状态

  • 第三次挥手:TCP服务器进程到TCP客户进程这个方向的连接还没有关闭。还可以发送数据。发送完成之后,应用层就通知TCP服务端进程关闭TCP连接。TCP服务器进程发送TCP连接释放报文段,FIN=1,ACK=1,seq=w,ack=u+1,进入最后确认状态

  • 第四次挥手:TCP客户端进程接收到TCP服务器发来的TCP连接释放报文段,发送一个ACK=1,seq=u+1,ack=w+1的普通TCP确认报文段,进入时间等待状态。TCP服务进程收到该确认报文段后就进入了关闭状态。TCP客户进程还要经过2MSL的时间才进入关闭状态

② 为什么第四次挥手时客户端不直接进入关闭状态?

  1. 客户端设置时间等待状态是为了确保服务端收到自己的TCP连接释放的确认报文段,而进入关闭状态。

    • 若第四次挥手时发送的TCP确认报文段丢失了,则会造成服务端超时重传,并且反复重传TCP连接释放请求而得不到响应,而无法进入关闭状态。
  2. 在时间等待状态持续期间(2MLS时长),可以使本次连接持续时间内所产生的所有报文段都从网络中消失。使下一个新的TCP连接中,不会出现旧连接中的报文段。

二、TCP流量控制和拥塞控制的区别

先介绍两个概念:输入负载和吞吐量

输入负载:代表单位时间内从网络输入的分组数量

吞吐量:代表单位时间内从网络输出的分组数量

1、流量控制的概念

TCP流量控制就是让发送方不要把数据发送得过快,要让接收方来得及接收,以免发送数据的丢失。

2、拥塞控制的概念

在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这就是拥塞。如带宽,交换机结点中的缓存和处理机等,都是网络的资源.

若出现拥塞而不进行控制,整个网路的吞吐量将随输入负载的增大而下降.

这就可能难以理解了。

3、举个例子

举一个例子:去游乐场坐过山车,人多了就会排队,外面排队的人就像输入负载,做完过山车的人就像吞吐量.

流量控制,就像控制排队队伍的长度。有一些排队的人,他们会等,但排队的人不能太多了,不然游客就不来了。回到网络中,输入负载过大,多出来的负载就会直接被丢弃,导致发送方要重新发送,这是TCP为什么要进行流量控制的原因.

拥塞控制,就像是坐过山车的时候,排队的人捣乱,例如插队什么的。排队的人就闹腾起来了,影响大家上车的速度,甚至大家都别玩了,使过山车停止运行。回到网络中,有这样的情况,明明有很高的输入负载,但吞吐量可能为0.如下图

三、TCP流量控制/滑动窗口协议

滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制

1、首先要知道TCP是面向字节流的

面向字节流是TCP实现可靠传输、流量控制以及拥塞控制的基础

TCP把应用进程交付下来的数据块仅仅看作是一连串的、无结构的字符流,TCP并不知道这些待传送的字节流的含义,仅将他们编号,并存储在自己的发送缓存中。TCP根据发送策略,从发送缓存中提取一定数量的字节,构建TCP报文段并发送。TCP接收方的TCP一方面从所接收到的TCP报文段中,取出数据载荷部分并存储在接收缓存中,另一方面将接收缓存中的一些字节交付给应用进程.

TCP不保证接收方的应用进程所收到的数据块与发送方应用进程所发出的数据块具有对应大小的关系.例如发送方应用进程交给发送发的TCP共10个数据块,但接收方的TCP可能只用了4个数据块就把收到的字节流交付给了上层的应用进程.但接收方应用进程收到的字节流必须和发送方应用进程发出的字节流完全一样(先交付了4个数据块,后面6个后面也要交付).当然,接收方的应用进程必须有能力识别收到的字节流,把他还原成有意义的应用层数据.也就是说,TCP是面向字节流的.这正是TCP实现可靠传输、流量控制以及拥塞控制的基础。

2、滑动窗口协议如何进行流量控制

接收方通过发送带有rwnd首部字段的确认报文段对发送方进行流量控制.

我们假设有两台主机,A要给B发送1000字节数据,主机B将会对A进行流量控制

  1. 首先,建立TCP连接时,B会告知A“我的接收窗口为400”(体现在TCP首部中的rwnd=400),A就把自己的发送窗口值设置为400,并在发送窗口里面存入400字节的数据

    • 这里出现了两个词:接收窗口和发送窗口。
    • 接收窗口为400就是B告诉A把发送窗口设为400.发送窗口意味着主机A在未收到主机B发来的确认时,可以将发送窗口中的全部数据(400字节)发送出去(但不是一次发400个字节)
  2. A的TCP把1~100字节的数据封装成TCP数据报文,发送给B的TCP.数据报的seq=1

    • 这里出现了seq,seq表示TCP报文的数据载荷部分第一个字节的序号是1.
  3. 假设1-100字节的数据还没到达B(假设数据封装的速度>数据传输的速度),A的TCP又把101~200字节的数据封装成TCP数据报文,发送给B的TCP.此时的seq=101

  4. 假设前面的数据都没有到达B,A的TCP又把201~300的数据发送给B.seq=201

  5. 主机B先后接收到1-100字节和101-200字节的两个数据报.然后对收到的201号以前的数据进行累计确认,将接收窗口调整为300.置ACK=1,ack=201,rwnd=300

    • 这里出现了ACK,而ACK=1表示这是一个确认报文段.而ack=201表示一个期望.由于主机B接收到了1100以及101200的数据,ack=201表示主机B期望下一个数据报的seq为201.rwnd为300表示希望主机A把它的发送窗口设置为300
  6. 主机A收到ACK=1和ack=201,即收到了1-201的累计确认.把发送窗口里面的1-200字节的数据丢弃,再把401-600的数据放入发送窗口里面.发现rwnd为300,就把发送窗口改为300,然后把501-600的数据移出来放回原处而不丢弃.目前,发送窗口里面的数据为201-500这300个字节的数据,其中201~300是已经发送但是未收到确认的数据.

    • 对于201-300字节的数据,A不知道B接收到了没,B也不知道A发送了没.对于这种情况,发送方A有超时重传机制,即一定时间内未收到ACK=1和seq=301的确认报文就会认为201-300的数据丢失.
  7. 主机A继续发送301-400的数据

  8. 主机A又发送401-500的数据

    • 此时,发送窗口内的全部数据都已经发送出去了,A能做的只有重传和等待B的响应

    • 由于B没有收到seq=201的数据报,所以不能发挥这个数据报的响应,所以也不能发回对301或401数据报的响应

    • 因为seq=201的数据报没有收到响应,所以最终会重传这个数据报

  9. 又过了一段时间,A选择重传201数据报

  10. 主机B收到重传的seq201报文段之后,对主机A所发送的501号以前的报文段进行累计确认,发回ACK=1,ack=501,rwnd=100,将接收窗口的值置为100

  11. A收到ack=501,则把501之前的报文全部丢弃,把501~600的数据拿进来.

  12. 主机A发送seq=501的数据报

  13. 主机B发送ACK=1,ack=601,rwnd=0

    • rwnd调整为0说明,主机B的接收缓存不够了
  14. 主机B收到ACK=1和ack=601,丢弃了601之前的数据,看到rwnd,把自己的窗口调节为0,并启动持续计时器此时主机A已经不能发送数据了.

  15. 假设主机B又来了一些缓存.将rwnd=300发送给主机A,正常的话将A将把自己的发送窗口设置为300.

  16. 正常的话A就会把自己的窗口调整为300并继续发送数据,但是假设这个rwnd=300的数据报丢失了,此时持续计时器就要发挥作用了。主机A会发送0窗口探测报文,仅携带1字节的数据,B在确认这个探测报文的时候,会给出自己现在的接收窗口值,如果接收窗口现在任然是0(ACK=1,rwnd=0),就重新启动持续计时器,如果接收窗口不是0,死锁的局面就打破了。

    • 解释一下为什么主机B的接收窗口为0,却能接收1个字节的数据?TCP规定:即使接收窗口为0,也必须要接收零窗口探测报文段确认报文段,以及携带有紧急数据的报文段
    • 如果零窗口探测报文段丢失了怎么办?答案是重传计时器

三、TCP的拥塞控制/TCP拥塞控制算法

1、TCP拥塞控制算法

前面流量控制的时候说过发送窗口是通过接收窗口来控制的.实际上发送窗口并不一定等于接收窗口的值,而是取拥塞窗口和接收窗口的较小值.即swnd=min{cwnd,rwnd}

cwnd是根据接收方的接收缓存来的,而rwnd是根据下面的拥塞控制算法来的.

假设cwnd永远小于rwnd,即发送窗口swnd永远取拥塞窗口cwnd的值,这是为了控制单一变量,防止rwnd的干扰

①慢开始算法和拥塞避免算法

慢开始指一开始向网络注入的报文段很少,但是cwnd增长却是非常快的

拥塞避免也并非完全避免拥塞,而是指在拥塞避免阶段将拥塞窗口控制为按线型规律增长,是网络比较不容易出现拥塞

何时使用拥塞避免?何时使用慢开始?
  • 发送方维持了一个慢开始门限ssthresh
  • 当cwnd小于ssthresh时,使用慢开始算法;当cwnd大于ssthresh时,使用拥塞避免算法.两者相等随便取.

首先发送方会自动设置拥塞窗口的初始值和慢开始门限

假设拥塞窗口的初始值为1,慢开始门限ssthresh=16

慢开始算法阶段
  • 发送方每次接收到接收方的确认报文时,就把自己的拥塞窗口乘以二
轮次cwndssthresh
0116
1216
2416
3816
41616

此时cwnd=ssthresh,改用拥塞避免算法

拥塞避免算法阶段
  • 每次发送方接收到接收方的确认报文段时,就把自己的拥塞窗口加一
  • 若发生了发送数据的丢失,则会触发超时重传机制
  • 超时重传时
    1. 将ssthresh的值更新为发生拥塞时cwnd的一半
    2. 讲cwnd值减少为1,并重新开始执行慢开始算法
轮次cwndssthresh
51716
61816
71916
82016
92116
102216
112316
12(假设此时发生了数据的丢失)2416
13(这里开始执行慢开始算法)112
14212
15412
16812
1712(注意这里是12不是16)12
18(这里又执行拥塞避免算法)1312
② 快重传算法和快恢复算法

由于只要报文段丢失,就会改用慢开始算法,仅仅通过报文段丢失就认为网络拥塞是不准确的.因为在网络中个别报文段丢失是很正常的,但实际上网络并没有拥塞.此时却改用慢开始算法是不准确的.

于是就有了快重传算法和快恢复算法

快重传,就是使发送方尽快进行重传,而不是等超时重传计时器超时就重传

  1. 假设发送方发送了n个报文段,并收到了前n-1个确认报文段,而第n个报文段丢失了
  2. 发送方发送第n+1个报文段
  3. 第n+1个报文段到达了
  4. 接收方发现到达了第n+1个,而没有收到第n个,就会发送对n-1个报文段的累计确认(ack=n),表明期望下一个报文段是第n个报文段.收到了n+2时再发送ack=n,收到了n+3时再发送ack=n.总之你发送方必须把第n个报文段给我发过来.
  5. 当发送发接累计收到了三次ack=n就知道你没接收到第n个报文段,就重新发送第n个报文段.此时超时重传计时器没到时间,拥塞控制算法不会改为慢开始算法.

使用快重传可以使整个网络的吞吐量提高约20%

发送发一旦收到三个重复确认,就执行快恢复算法

  1. 发送方将慢开始门限sshresh值和拥塞窗口cwnd值调整为当前窗口的一半,仍执行拥塞避免算法
  2. 也有的快恢复实现是吧快恢复开始时的拥塞窗口cwnd的值再增大一些,即cwnd=ssthred(先执行第一条)+3