1、TCP三个阶段

TCP整体介绍

  TCP是属于网络分层中的传输层,因为OSI分为7层,感觉太麻烦了,所以分为四层就好了,简单。
分层以及每层的协议,TCP是属于传输层,如下两张图:

   

 

   

 

 

   

 

一、建立连接(三次握手)

  

  第一次握手:客户主动(active open)去connect服务器,并且发送SYN 假设序列号为J,服务器是被动打开(passive open)

  第二次握手:服务器在收到SYN后,它会发送一个SYN以及一个ACK(应答)给客户,ACK的序列号是 J+1表示是给SYN J的应答,新发送的SYN K 序列号是K

  第三次握手:客户在收到新SYN K, ACK J+1 后,也回应ACK K+1 以表示收到了,然后两边就可以开始数据发送数据了

TCP三次握手详细解析过程:

  

 

 

  第一次握手:

  客户在socket() connect()后主动(active open)连接上服务器, 发送SYN ,这时客户端的状态是SYN_SENT
服务器在进行socket(),bind(),listen()后等待客户的连接,收到客户端的 SYN 后,

  1.半连接队列(syn queue)未满

    服务器将该连接的状态变为SYN_RCVD, 服务器把连接信息放到半连接队列(syn queue)里面。

  2.半连接队列(syn queue)已满

    服务器不会将该连接的状态变为SYN_RCVD,且将该连接丢弃(SYN flood攻击就是利用这个原理,对于SYN foold攻击,应对方法之一是使syncookies生效,将其值置1即可,路径/proc/sys/net/ipv4/tcp_syncookies,即使是半连接队列syn queue已经满了,也可以接收正常的非恶意攻击的客户端的请求,但是这种方法只在无计可施的情况下使用,man tcp里面的解析是这样说的,

  查看半连接队列最大个数:cat /proc/sys/net/ipv4/tcp_max_syn_backlog

  

  SYN flood攻击:

    攻击方的客户端只发送SYN分节给服务器,然后对服务器发回来的SYN+ACK什么也不做,直接忽略掉,不发送ACK给服务器;这样就可以占据着服务器的半连接队列的资源,导致正常的客户端连接无法连接上服务器。-----[维基百科]

    (SYN flood攻击的方式其实也分两种,第一种,攻击方的客户端一直发送SYN,对于服务器回应的SYN+ACK什么也不做,不回应ACK, 第二种,攻击方的客户端发送SYN时,将源IP改为一个虚假的IP, 然后服务器将SYN+ACK发送到虚假的IP, 这样当然永远也得不到ACK的回应。)

  第二次握手:

    服务器返回SYN+ACK给到客户端,客户端收到SYN+ACK后,状态从SYN_SENT变为ESTABLISHED,也即是connect()函数的返回。

  第三次握手:

  全连接队列(accept queue)的最大值 /proc/sys/net/core/somaxconn (默认128)

  

 

  1.全连接队列(accept queue)未满   

    服务器收到客户端发来的ACK, 服务端该连接的状态从SYN_RCVD变为ESTABLISHED,然后服务器将该连接从半连接队列(syn queue)里面移除,且将该连接的信息放到全连接队列(accept queue)里面。

  2.全连接队列(accept queue)已满    

    服务器收到客户端发来的ACK, 不会将该连接的状态从SYN_RCVD变为ESTABLISHED。当然全连接队列(accept queue)已满时,则根据 tcp_abort_on_overflow 的值来执行相应动作

  /proc/sys/net/ipv4/tcp_abort_on_overflow 查看参数值

  

 

   2.1   tcp_abort_on_overflow = 0

   则服务器建立该连接的定时器,

  这个定时器是一个服务器的规则是从新发送syn+ack的时间间隔成倍的增加,比如从新了第二次握手,进行了5次,这五次的时间分别是 1s, 2s,4s,8s,16s,
这种倍数规则叫“二进制指数退让”(binary exponential backoff)

给客户端定时从新发回SYN+ACK即从新进行第二次握手,(如果客户端设定的超时时间比较短就很容易出现异常)

服务器从新进行第二次握手的次数/proc/sys/net/ipv4/tcp_synack_retries

  

 

   2.2    tcp_abort_on_overflow = 1

  当 tcp_abort_on_overflow 等于1 时,重置连接(一般是发送RST给客户端)

 

二、传输过程

  建立连接后,两台主机就可以相互传输数据了。如下图所示:

    

 

  上图给出了主机A分2次(分2个数据包)向主机B传递200字节的过程。首先,主机A通过1个数据包发送100个字节的数据,数据包的 Seq 号设置为 1200。主机B为了确认这一点,向主机A发送 ACK 包,并将 Ack 号设置为 1301。

为了保证数据准确到达,目标机器在收到数据包(包括SYN包、FIN包、普通数据包等)包后必须立即回传ACK包,这样发送方才能确认数据传输成功。

  此时 Ack 号为 1301 而不是 1201,原因在于 Ack 号的增量为传输的数据字节数。假设每次 Ack 号不加传输的字节数,这样虽然可以确认数据包的传输,但无法明确100字节全部正确传递还是丢失了一部分,比如只传递了80字节。因此按如下的公式确认 Ack 号:

Ack号 = Seq号 + 传递的字节数 + 1

  与三次握手协议相同,最后加 1 是为了告诉对方要传递的 Seq 号。

下面分析传输过程中数据包丢失的情况,如下图所示:

    

 

   上图表示通过 Seq 1301 数据包向主机B传递100字节的数据,但中间发生了错误,主机B未收到。经过一段时间后,主机A仍未收到对于 Seq 1301 的ACK确认,因此尝试重传数据。

  为了完成数据包的重传,TCP套接字每次发送数据包时都会启动定时器,如果在一定时间内没有收到目标机器传回的 ACK 包,那么定时器超时,数据包会重传。

  上图演示的是数据包丢失的情况,也会有 ACK 包丢失的情况,一样会重传。

重传超时时间(RTO, Retransmission Time Out):

  这个值太大了会导致不必要的等待,太小会导致不必要的重传,理论上最好是网络 RTT 时间,但又受制于网络距离与瞬态时延变化,所以实际上使用自适应的动态算法(例如 Jacobson 算法和 Karn 算法等)来确定超时时间。

重传次数:

  TCP数据包重传次数根据系统设置的不同而有所区别。有些系统,一个数据包只会被重传3次,如果重传3次后还未收到该数据包的 ACK 确认,就不再尝试重传。但有些要求很高的业务系统,会不断地重传丢失的数据包,以尽最大可能保证业务数据的正常交互。

  最后需要说明的是,发送端只有在收到对方的 ACK 确认包后,才会清空输出缓冲区中的数据。

 

三、断开连接

 建立连接需要三次握手,断开连接需要四次握手,可以形象的比喻为下面的对话:

  • [Shake 1] 套接字A:“任务处理完毕,我希望断开连接。”
  • [Shake 2] 套接字B:“哦,是吗?请稍等,我准备一下。”
  • 等待片刻后……
  • [Shake 3] 套接字B:“我准备好了,可以断开连接了。”
  • [Shake 4] 套接字A:“好的,谢谢合作。”


下图演示了客户端主动断开连接的场景:

    

 

 

建立连接后,客户端和服务器都处于ESTABLISED状态。这时,客户端发起断开连接的请求:

1) 客户端调用 close() 函数后,向服务器发送 FIN 数据包,进入FIN_WAIT_1状态。FIN 是 Finish 的缩写,表示完成任务需要断开连接。

2) 服务器收到数据包后,检测到设置了 FIN 标志位,知道要断开连接,于是向客户端发送“确认包”,进入CLOSE_WAIT状态。

注意:服务器收到请求后并不是立即断开连接,而是先向客户端发送“确认包”,告诉它我知道了,我需要准备一下才能断开连接。

3) 客户端收到“确认包”后进入FIN_WAIT_2状态,等待服务器准备完毕后再次发送数据包。

4) 等待片刻后,服务器准备完毕,可以断开连接,于是再主动向客户端发送 FIN 包,告诉它我准备好了,断开连接吧。然后进入LAST_ACK状态。

5) 客户端收到服务器的 FIN 包后,再向服务器发送 ACK 包,告诉它你断开连接吧。然后进入TIME_WAIT状态。

6) 服务器收到客户端的 ACK 包后,就断开连接,关闭套接字,进入CLOSED状态。

关于 TIME_WAIT 状态的说明

客户端最后一次发送 ACK包后进入 TIME_WAIT 状态,而不是直接进入 CLOSED 状态关闭连接,这是为什么呢?

  TCP 是面向连接的传输方式,必须保证数据能够正确到达目标机器,不能丢失或出错,而网络是不稳定的,随时可能会毁坏数据,所以机器A每次向机器B发送数据包后,都要求机器B”确认“,回传ACK包,告诉机器A我收到了,这样机器A才能知道数据传送成功了。如果机器B没有回传ACK包,机器A会重新发送,直到机器B回传ACK包。

  客户端最后一次向服务器回传ACK包时,有可能会因为网络问题导致服务器收不到,服务器会再次发送 FIN 包,如果这时客户端完全关闭了连接,那么服务器无论如何也收不到ACK包了,所以客户端需要等待片刻、确认对方收到ACK包后才能进入CLOSED状态。那么,要等待多久呢?

  数据包在网络中是有生存时间的,超过这个时间还未到达目标主机就会被丢弃,并通知源主机。这称为报文最大生存时间(MSL,Maximum Segment Lifetime)。TIME_WAIT 要等待 2MSL 才会进入 CLOSED 状态。ACK 包到达服务器需要 MSL 时间,服务器重传 FIN 包也需要 MSL 时间,2MSL 是数据包往返的最大时间,如果 2MSL 后还未收到服务器重传的 FIN 包,就说明服务器已经收到了 ACK 包。

 

posted @ 2021-04-07 00:01  zwj鹿港小镇  阅读(1619)  评论(0编辑  收藏  举报