TCP协议

少于 1 分钟读完

TCP是一个非常复杂的协议,网络编程中要用到很多TCP的知识,更好的理解TCP有利于分析程序运行,发现问题,掌握TCP协议是一个网络编程人员的必备技能。

网络报头

TCP格式如下: TCP报头 注意

  • 一个TCP连接需要四个元组来表示是同一个连接(src_ip, src_port, dst_ip, dst_port),IP地址在IP层已经制定,此处TCP层只需要端口号信息。
  • Sequence Number是包的序号,用来解决网络包乱序(reordering)问题。Acknowledgement Number就是ACK——用于确认收到,用来解决不丢包的问题。 Window又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。
  • TCP Flag ,也就是包的类型,主要是用于操控TCP的状态机的。

解释请看下图: TCP报头

TCP的状态转换

TCP状态主要发生在三个地方:连接建立、收发数据和连接关闭。 首先看看TCP的状态转换图,连接关闭是状态变化非常的复杂: 状态转换

报文交换图: 报文交换

关闭时需要4次挥手是因为TCP是全双工的,关闭一端就需要2次交互。

注意

  • 同时关闭和同时建立连接是非常特殊的情况,TCP也能够正确处理。由于在实际中很少发生这种情况,暂且不做介绍。
  • ISN是不能hard code的,不然会出问题的。RFC793中说,ISN会和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。因为,我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime。
  • TIME_WAIT状态:1)TIME_WAIT确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到Ack,就会触发被动端重发Fin,一来一去正好2个MSL,2)有足够的时间让这个连接不会跟后面的连接混在一起。

TCP的重传机制

。TCP协议要求在发送端每发送一个报文段,就启动一个定时器并等待确认信息;接收端成功接收新数据后返回确认信息。若在定时器超时(Timeout)前数据未能被确认,TCP就认为报文段中的数据已丢失或损坏,需要对报文段中的数据重新组织和重传。

TCP的RTT算法

从前面的TCP重传机制我们知道Timeout的设置对于重传非常重要。

  • 设长了,重发就慢,丢了老半天才重发,没有效率,性能差;
  • 设短了,会导致可能并没有丢就重发。于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

而且,这个超时时间在不同的网络的情况下,根本没有办法设置一个死的值。只能动态地设置。 为了动态地设置,TCP引入了RTT——Round Trip Time,也就是一个数据包从发出去到回来的时间。这样发送端就大约知道需要多少的时间,从而可以方便地设置Timeout——RTO(Retransmission TimeOut),以让我们的重传机制更高效。

TCP的滑动窗口

TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

滑动窗口

上图中分成了四个部分,分别是:(其中那个黑模型就是滑动窗口)

  • #1已收到ack确认的数据。
  • #2发还没收到ack的。
  • #3在窗口中还没有发出的(接收方还有空间)。
  • #4窗口以外的数据(接收方没空间)

注意 如果Window变成0了,TCP会怎么样? TCP使用了Zero Window Probe技术,缩写为ZWP,也就是说,发送端在窗口变成0后,会发ZWP的包给接收方,让接收方来ack他的Window尺寸,一般这个值会设置成3次,第次大约30-60秒(不同的实现可能会不一样)。如果3次过后还是0的话,有的TCP实现就会发RST把链接断了。

Silly Window Syndrome问题 Silly Windows Syndrome指的是报文中数据特别的少,只有几个字节,为了发送这几个字节,需要发送TCP+IP头有40个字节,这是一种特别浪费带宽的。这个现像就像是你本来可以坐200人的飞机里只做了一两个人。

TCP的拥塞处理 – Congestion Handling

拥塞控制主要是四个算法:1)慢启动,2)拥塞避免,3)拥塞发生,4)快速恢复。

慢热启动算法 – Slow Start

慢启动的算法如下(cwnd全称Congestion Window):

  1. 连接建好的开始先初始化cwnd = 1,表明可以传一个MSS大小的数据。
  2. 每当收到一个ACK,cwnd++; 呈线性上升。
  3. 每当过了一个RTT,cwnd每当过了一个RTT,cwnd = cwnd*2; 呈指数让升。
  4. 还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”

拥塞避免算法 – Congestion Avoidance

前面说过,还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”。一般来说ssthresh的值是65535,单位是字节,当cwnd达到这个值时后,算法如下:

  1. 收到一个ACK时,cwnd = cwnd + 1/cwnd
  2. 当每过一个RTT时,cwnd = cwnd + 1

这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。很明显,是一个线性上升的算法。

拥塞状态时的算法

超时时的算法如下:

  1. 把ssthresh降低为cwnd值的一半
  2. 把cwnd重新设置为1
  3. 重新进入慢启动过程。

收到3个相同的ACK时的算法如下:

  1. 把ssthresh设置为cwnd的一半
  2. 把cwnd再设置为ssthresh的值(具体实现有些为ssthresh+3)
  3. 重新进入拥塞避免阶段。

快速恢复算法 – Fast Recovery

Fast Recovery算法如下:

  1. cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
  2. 重传Duplicated ACKs指定的数据包
  3. 如果再收到 duplicated Acks,那么cwnd = cwnd +1
  4. 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。

分类:

更新时间: