TCP协议

什么是TCP协议

TCP协议即Transmission Control Protocol传输控制协议,主要用于提供可靠的通信传输,它是面向连接的、可靠的流协议。它的特点是具有顺序控制或重发控制机制、具备流量控制、拥塞控制、提高网络利用率。

TCP协议的特点及实现

TCP如何提高可靠性

TCP通信中通过返回确认应答来提高可靠性。当发送端的数据到达接收主机时,接收主机需要返回确认的ACK(Positive Acknowledgement)来确认。
如果接收主机没有返回ACK或者等待时间过长,说明数据很可能丢失。此时发送端会认为数据丢失并进行重发,因此即使存在丢包,仍然会保证数据继续发送直至抵达目的主机。
不过由于没有ACK确认也可能是因为接收主机在返回ACK的途中丢失,此时发送端会重复发送同样内容,导致接收主机反复接收相同的数据。
此外,也有一些原因导致ACK延迟抵达,在发送端开始重试之后才收到确认应答。每次通信中会包含序列号,序列号是按照顺序给发送数据的每一个字节(8位字节)都标上号码的编号,接收端根据TCP首部中的序列号和数据的长度,确认下一次通信应该接收的序号作为确认应答返回。通过序列号和确认应答号,TCP可以实现可靠传输。

超时重发

TCP的超时重发不是固定的等待时间间隔的。由于网络环境的影响,需要确认一个合适的等待时间进行重发。因此,TCP在每次通信中都会记录往返花费的时间(RTT,Round Trip Time)以及其偏差(波动值、方差,也称抖动)。通过往返时间和偏差,重发的超时时间就是比这个的总和稍大的数值。
在BSD的Unix以及Windows中,超时都以0.5秒为单位进行控制。由于最初的数据包不知往返时间,一般设置为6秒左右。
在重发后仍收不到ACK,则等待时间会以2倍、4倍的指数函数延长。达到一定次数以后会判定异常关闭连接。

连接管理

TCP通过三次握手建立连接,通过四次挥手断开连接。

三次握手

  • 客户端发送SYN包请求建立连接
  • 服务端返回ACK确认与客户端建立连接,同时发送SYN包请求建立连接
  • 客户端发送ACK包确认与服务端建立连接
三次握手实例

https://tools.ietf.org/html/rfc793#section-3.4

协议3-4部分详细说明了建立连接所要进行的三次握手细节。

      TCP A                                                TCP B

 1.  CLOSED                                               LISTEN

 2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED

 3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED

 4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED

 5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED

          Basic 3-Way Handshake for Connection Synchronization

基础的三次握手:
2. A发送序列号为100的SYN包给B
3. B发送对序列号100的确认应答ACK,期望收到A序列号101的包,同时发送SYN包
4. A发送对序列号300的ACK包(301),此处包含的是空segment
5. A发送数据包,此时SEQ与4一样,因为4没有占用SEQ的空间

      TCP A                                            TCP B

  1.  CLOSED                                           CLOSED

  2.  SYN-SENT     --> <SEQ=100><CTL=SYN>              ...

  3.  SYN-RECEIVED <-- <SEQ=300><CTL=SYN>              <-- SYN-SENT

  4.               ... <SEQ=100><CTL=SYN>              --> SYN-RECEIVED

  5.  SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...

  6.  ESTABLISHED  <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED

  7.               ... <SEQ=101><ACK=301><CTL=ACK>     --> ESTABLISHED

                Simultaneous Connection Synchronization

双方同时发起的握手请求:
按照上面的回话过程,会产生混淆,两端建立的TCP连接实际并不同步,而是各自对应序列号100、300的连接。
为了解决这个问题,当收到的ACK与发送出去的SYC不对应时,一个特殊的控制信号就会被发出:reset。
当TCP处于非同步状态(如SYN-SENT、SYN-RECEIVED)收到reset时,它会转为LISTEN状态。如果TCP处于同步状态(如ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT),它会终止连接并通知用户。

      TCP A                                                TCP B

  1.  CLOSED                                               LISTEN

  2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               ...

  3.  (duplicate) ... <SEQ=90><CTL=SYN>               --> SYN-RECEIVED

  4.  SYN-SENT    <-- <SEQ=300><ACK=91><CTL=SYN,ACK>  <-- SYN-RECEIVED

  5.  SYN-SENT    --> <SEQ=91><CTL=RST>               --> LISTEN


  6.              ... <SEQ=100><CTL=SYN>               --> SYN-RECEIVED

  7.  SYN-SENT    <-- <SEQ=400><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED

  8.  ESTABLISHED --> <SEQ=101><ACK=401><CTL=ACK>      --> ESTABLISHED

                    Recovery from Old Duplicate SYN

从重复SYN信息中恢复正常连接:
3.A发送了2个SYN包给B,序列号依次为90,100。此时B接收到了90,处于SYN-RECEIVED状态
4.B ACK了序列号为90的SYN请求,此时A收到的ACK与发送的SYN(100)不对应,因此发送RST,要求reset
5.B收到RST后处于LISTEN状态
6.此时B收到了A序列号为100的SYN请求
7.B对序列号100的SYN请求进行ACK
8.A收到正确的ACK,建立连接,并发送ACK给B,B建立连接

当TCP连接处于半开状态(half-open)时,也就是任意一方终断连接而另一方不知道,或者两方处于不同步状态时,如果尝试通信,将会导致RST的发送。
当一方的TCP连接Crash后,如A此时打算重新开始,或者从上一次的某点继续发送数据,A就会重新建立连接,或者向它认为还没关闭的(上一次的)连接发送数据。后一种情况中A会从自己的TCP连接中收到“connection not open”的信息。前一种情况中A发送建立连接的SYN,此时B认为之前的连接依然有效:

      TCP A                                           TCP B

  1.  (CRASH)                               (send 300,receive 100)

  2.  CLOSED                                           ESTABLISHED

  3.  SYN-SENT --> <SEQ=400><CTL=SYN>              --> (??)

  4.  (!!)     <-- <SEQ=300><ACK=100><CTL=ACK>     <-- ESTABLISHED

  5.  SYN-SENT --> <SEQ=100><CTL=RST>              --> (Abort!!)

  6.  SYN-SENT                                         CLOSED

  7.  SYN-SENT --> <SEQ=400><CTL=SYN>              -->

                     Half-Open Connection Discovery

4.B返回ACK=100,与A发送的序列号400的SYN不对应
5.因此A会发送RST,B收到RST后会关闭原连接(从同步态转入CLOSED,而非LISTEN)

        TCP A                                              TCP B

  1.  (CRASH)                                   (send 300,receive 100)

  2.  (??)    <-- <SEQ=300><ACK=100><DATA=10><CTL=ACK> <-- ESTABLISHED

  3.          --> <SEQ=100><CTL=RST>                   --> (ABORT!!)

           Active Side Causes Half-Open Connection Discovery

在A崩溃恢复后,此时A已经不再持有原连接,B仍以为连接生效,尝试向A发送数据。此时A时不会接收数据的因为连接并不存在,因此向B发送RST终止连接。

四次挥手

  • 客户端发送FIN包请求断开连接
  • 服务端发送ACK包应答,确认断开连接
  • 服务端发送FIN包请求断开连接
  • 客户端发送ACK包应答,确认断开连接
四次挥手实例

四次挥手可以简单分为几种情况:

  1. 发送端提出FIN
    发送方发出FIN报文后进入FIN-WAIT-1状态,不再发送数据,等待对方确认,此时仍然可以接收数据。收到ACK后进入FIN-WAIT-2状态,此时等待对方将剩余数据发送完毕后发送FIN报文。当收到对方FIN报文后,进入TIME-WAIT状态,发送ACK确认对方报文,等待2MSL时间后关闭连接。
  2. 接收端收到FIN
    接收方接到FIN报文后,发送ACK进行确认,同时将buffer区域的剩余数据发送完毕后发出FIN报文请求关闭连接。当接收到对方的ACK报文后,关闭连接。如果ACK一直没有到达,在超时后会告知用户关闭连接。
  3. 双方同时提出FIN
    此时双方交换FIN报文,并且都互相ACK对方报文,当收到对方ACK后,进入TIME-WAIT状态,在2MSL后关闭连接。
      TCP A                                                TCP B

  1.  ESTABLISHED                                          ESTABLISHED

  2.  (Close)
      FIN-WAIT-1  --> <SEQ=100><ACK=300><CTL=FIN,ACK>  --> CLOSE-WAIT

  3.  FIN-WAIT-2  <-- <SEQ=300><ACK=101><CTL=ACK>      <-- CLOSE-WAIT

  4.                                                       (Close)
      TIME-WAIT   <-- <SEQ=300><ACK=101><CTL=FIN,ACK>  <-- LAST-ACK

  5.  TIME-WAIT   --> <SEQ=101><ACK=301><CTL=ACK>      --> CLOSED

  6.  (2 MSL)
      CLOSED

                         Normal Close Sequence

      TCP A                                                TCP B

  1.  ESTABLISHED                                          ESTABLISHED

  2.  (Close)                                              (Close)
      FIN-WAIT-1  --> <SEQ=100><ACK=300><CTL=FIN,ACK>  ... FIN-WAIT-1
                  <-- <SEQ=300><ACK=100><CTL=FIN,ACK>  <--
                  ... <SEQ=100><ACK=300><CTL=FIN,ACK>  -->

  3.  CLOSING     --> <SEQ=101><ACK=301><CTL=ACK>      ... CLOSING
                  <-- <SEQ=301><ACK=101><CTL=ACK>      <--
                  ... <SEQ=101><ACK=301><CTL=ACK>      -->

  4.  TIME-WAIT                                            TIME-WAIT
      (2 MSL)                                              (2 MSL)
      CLOSED                                               CLOSED

                      Simultaneous Close Sequence

TCP分段发送数据

TCP中的包的长度最理想的情况(最大消息长度:MSS,Maximum Segment Size)应该是正好在IP中部倍分片处理的最大数据长度。TCP在传送数据时会以MSS为单位进行分割,在重发中也是以MSS为单位。
MSS的确定是通过在三次握手过程中,客户端与服务端都会在TCP首部中写入MSS选项,取两者中较小值作为实际使用的MSS值。此时握手过程中的TCP包长度+4,以包含MSS信息。

利用窗口控制提高速度

为了解决对每次通信的包均进行确认应答带来的性能影响,TCP引入窗口概念,确认应答由每段MSS改为对数段进行应答。
发送端无需等待接收端的ACK即可继续发送下一段报文,如:
假设MSS=1000,发送端发送1-1000、1001-2000、2001-3000、3001-4000给接收端,当接收端确认这4端通信后再继续发送后续数据。
此时窗口大小为4段(MSS)。
在整给窗口的确认应答没有到达之前,如果其中部分数据出现丢包,那么发送端仍然要负责重传。
在收到确认应答后,将窗口滑动至确认应答中的序列号位置,这种机制也被称为滑动窗口控制

滑动窗口与重发控制

在滑动窗口中不可避免也会有丢包问题,以上面例子为例,分为以下几种情况:

  • 接收端接收成功但其中一段ACK报文丢失

此时如果中间的确认应答丢失,由于对于接收端的视角来说,全部报文都正常接收,因此会正常相应给发送端,因此发送确认报文:下一个是1001,下一个是2001(丢失),下一个是3001,下一个是4001。对于发送端而言,无需理会中途是否丢失ACK报文,只要收到后续的ACK,即可认为前面的报文已经成功传达。

  • 其中一段报文没有抵达接收端

此时假定丢失了第二段报文,那么接收端由于报文缺失,会在ACK中告知发送端:下一个是1001,下一个是1001,下一个是1001。如果发送端连续3次收到同一个ACK应答,就会对此段数据进行重发。当丢失的报文接收成功时,对于接收端而言,目前1-4000的报文均已接收到,因此返回的ACK将会是:下一个是4001。

为什么是3次才进行重发,而不是2次或者4次?

由于发送端在窗口限度内发送了多段报文,当接收端返回ACK 1001时,发送端并不知道接收端请求1001是因为1001-2000段报文丢失,还是说1001-2000段报文因为一些原因在接收端接收顺序异常(例如最后才接收到此段报文,那么前面接收的报文都会返回ACK 1001)。当第一次接收到重复的 ACK 1001时,发送端会假设这是因为报文顺序被打乱造成的,因此会选择继续等待看是否会再有重复报文发来,如果是因为乱序,那么下面几次后应该会接收到新的ACK,因为缺失的报文已经被补全。但若接收到连续3段或更多的重复ACK报文,则很有可能该段ACK请求的报文在途中丢失,此时TCP会选择重发此段报文。这种机制被称为高速重发控制(Fast Retransmission)

http://www.faqs.org/rfcs/rfc2001.html
3. Fast Retransmit
Modifications to the congestion avoidance algorithm were proposed in 1990 [3]. Before describing the change, realize that TCP may generate an immediate acknowledgment (a duplicate ACK) when an out- of-order segment is received (Section 4.2.2.21 of [1], with a note that one reason for doing so was for the experimental fast- retransmit algorithm). This duplicate ACK should not be delayed. The purpose of this duplicate ACK is to let the other end know that a segment was received out of order, and to tell it what sequence number is expected.
Since TCP does not know whether a duplicate ACK is caused by a lost segment or just a reordering of segments, it waits for a small number of duplicate ACKs to be received. It is assumed that if there is just a reordering of the segments, there will be only one or two duplicate ACKs before the reordered segment is processed, which will then generate a new ACK. If three or more duplicate ACKs are received in a row, it is a strong indication that a segment has been lost. TCP then performs a retransmission of what appears to be the missing segment, without waiting for a retransmission timer to expire.

流控制

接收端通过告知发送端自己可以接收的数据大小,发送端会发送不超过这个限度的数据。这个大小限度称为窗口大小
TCP首部中有对应字段存放窗口大小。
当缓冲区面临数据溢出时,窗口大小会被设置成一个更小的值通知发送端。发送端根据指示对发送数据量进行控制。这也就形成了一个完整的TCP流控制(流量控制)
当窗口大小为0时,即缓冲区满,下一次发送端在等待重发超时的时间以后(因为即使上一个包窗口为0,仍有可能处理完毕后在下一个包返回窗口大小的更新),会发送一个窗口探测的包(仅含一个字节),如发送4001-4001作为窗口探测。此时假设缓冲区满,继续返回0。发送端通过不断重试探测包,当接收端返回窗口大小不为0时,就会继续按照窗口大小正常发送数据包。

拥堵控制

为了避免网络阻塞以及启动时大量发送数据,TCP中定义了一个拥塞窗口的概念。
慢启动时,拥塞窗口大小设置为1个MSS大小,每收到一次ACK,拥塞窗口大小翻倍。
发送数据包时,数据包长度为拥塞窗口大小及接收端通知的窗口大小中的较小值。
当发生超时重发时,拥塞窗口重置为1,进行慢启动修正,减少了连续发包导致的网络拥堵。
由于随着确认应答,拥塞窗口大小指数增长,因此需要引入慢启动阈值,当窗口值超过阈值时,后续ACK只允许窗口以下面比例放大:MSS * MSS / 窗口大小
当窗口大至一定程度时,每次放大的比例甚至会小于一个MSS长度。
在初始化TCP通信时,阈值没有设定,只有当发生超时重发时,阈值才会被设定为拥塞窗口的一半大小。而又因为高速重发控制,在发生高速重发时会将窗口大小设置为当前窗口一半,并将阈值设置为窗口大小+3MSS。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值