TCP的三次握手与四次挥手

Posted by suleo on April 20, 2021

TCP的三次握手和四次挥手

TCP/IP的知识太多了,而对于我们不是专门做网络的来说,这个问题是被问到最多的

我接触的拓扑比较简单,排查通信问题基本也就是看看三次握手卡在谁那了,防火墙的策略,然后再继续排查,对于下列的问题,思考甚少,记录下来,也希望以后工作中能多接触点稍复杂的网络,更好的理解下四层的一些理论。

为什么连接的时候是三次握手?

什么是半连接队列?

ISN(Initial Sequence Number)是固定的吗?

三次握手过程中可以携带数据吗?

如果第三次握手丢失了,客户端服务端会如何处理?

SYN攻击是什么?

挥手为什么需要四次?

四次挥手释放连接时,等待2MSL的意义?

一、TCP通信

常见服务端口及应用的传输层协议
应用程序 FTP TFTP TELNET SMTP DNS HTTP SSH MYSQL
熟知端口 21,20 69 23 25 53 80 22 3306
传输层协议 TCP UDP TCP TCP UDP TCP TCP TCP
  • TCP是一种面向连接的单播协议,广播多播不能用于TCP,在发送数据前,通信双方必须在彼此间建立一条连接。“连接”其实就是客户端和服务器的内存里保存的一份关于对方的信息,如ip地址、端口号等。

  • TCP 使用校验和,确认和重传机制来保证可靠传输
  • TCP 给数据分节进行排序,并使用累积确认保证数据的顺序不变和非重复
  • TCP 使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制

  • TCP提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用4次挥手来关闭一个连接。

二、TCP连接 建立过程

三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包,主要是为了验证双方的接收能力和发送能力是否正常;指定自己的初始化序列号为后面的可靠性传送做准备;建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

tcp三次握手

  1. 刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。

  2. 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。初始序列号 seq=x ,此时客户端处于 SYN_SEND (同步已发送)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。

    首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。

  3. 第二次握手:服务器收到客户端的 SYN 报文之后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号

  4. 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。

三、三次握手相关问题

1. 为什么需要三次握手

为什么需要三次握手,两次不行吗? 弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

​ 第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端 的接收能力是正常的。

​ 第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端 的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

​ 第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服 务器自己的发送、接收能力也正常。

​ 因此,需要三次握手才能确认双方的接收与发送能力是否正常。

假设只有两次握手:

  1. 如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求
  2. 后来收到了确认,建立了连接
  3. 数据传输完毕后,就释放了连接
  4. 客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端
  5. 此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了
  6. 此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源

2. 什么是半连接队列?

半连接队列 :服务器第一次收到客户端的 SYN 报文之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列

全联接队列 :已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

SYN-ACK 重传次数的问题: 服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。 注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s……

3. ISN(Initial Sequence Number)是固定的吗?

  1. 当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN。

  2. ISN可以看作是一个32比特的计数器,每4ms加1 。

  3. 这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。

  4. 三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。

  5. 如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

4. 三次握手过程中可以携带数据吗?

第一次、第二次握手不可以携带数据,第三次握手的时候,是可以携带数据的

  1. 假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,重复高频的发 SYN 报文,会让服务器花费很多时间、内存空间来接收这些报文。

  2. 第三次握手的时候,此时客户端已经处于 ESTABLISHED 状态,并且也已经知道服务器的接收、发送能力是正常的了,所以可以直接携带数据。

5. SYN攻击是什么?

  1. 在三次握手过程中,服务器发送 SYN-ACK 之后,收到客户端的 ACK 之前的 TCP 连接称为半连接(half-open connect)。
  2. 此时服务器处于 SYN_RCVD 状态 , 只有当收到 ACK 后,服务器才能转入 ESTABLISHED 状态.
  3. SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包
  4. Server则需要回复确认包,并等待Client确认
  5. 由于源地址不存在,因此Server需要不断重发直至超时
  6. 伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃
  7. 从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
如何检测&&防御syn flood

判断:检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。可通过netstat查看netstat -n -p TCP | grep SYN_RECV

防护:缩短超时(SYN Timeout)时间;增加最大半连接数;过滤网关防护; SYN cookies技术

6. 连接过程中,客户端出现故障

  1. TCP还设有一个保活计时器

  2. 客户端如果出现故障,服务器不能一直等下去,白白浪费资源。

  3. 服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时

  4. 若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,发送一个 probe 包

    探测报文会给网络带来额外的流量,另外 TCP KeepAlive 只能在内核层级监测连接的存活与否,而连接的存活不一定代表服务的可用。例如当一个服务器 CPU 进程服务器占用达到 100%,已经卡死不能响应请求了,此时 TCP KeepAlive 依然会认为连接是存活的。因此 TCP KeepAlive 对于应用层程序的价值是相对较小的。需要做连接保活的应用层程序,例如 QQ,往往会在应用层实现自己的心跳功能。
  5. 以后每隔75秒发送一次,若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障

  6. 关闭连接

四、四次挥手

建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务器均可主动发起挥手动作。

tcp四次挥手

  1. 双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求

  2. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号
  3. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间
  4. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)
  5. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  6. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
  7. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

五、四次挥手相关问题

1. 挥手为什么需要四次

  1. 当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
  2. 但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,”你发的FIN报文我收到了”。
  3. 只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

2. 2MSL等待状态

  1. TIME_WAIT状态也成为2MSL等待状态。
  2. 每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。
  3. 这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。
  4. 对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。
  5. 这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
等待2MSL的意义?

MSL是Maximum Segment Lifetime的英文缩写,“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

  1. 为了保证客户端发送的最后一个ACK报文段能够到达服务器。
  2. 因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。
  3. 服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。
  4. 最后客户端和服务器都能正常的关闭。
  5. 假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。
    • 最后的ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认
    • 服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段
    • 接着客户端重传一次确认,重新启动2MSL计时器
    • 最后客户端和服务端都进入到CLOSED状态
    • 若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到服务端重传的FIN+ACK报文段
    • 所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态。
    • 防止“已失效的连接请求报文段”出现在本连接中。 客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失
    • 使下一个新的连接中不会出现这种旧的连接请求报文段。

六、UDP

  • UDP 缺乏可靠性。UDP 本身不提供确认,序列号,超时重传等机制。UDP 数据报可能在网络中被复制,被重新排序。即 UDP 不保证数据报会到达其最终目的地,也不保证各个数据报的先后顺序,也不保证每个数据报只到达一次
  • UDP 数据报是有长度的。每个 UDP 数据报都有长度,如果一个数据报正确地到达目的地,那么该数据报的长度将随数据一起传递给接收方。而 TCP 是一个字节流协议,没有任何(协议上的)记录边界。
  • UDP 是无连接的。UDP 客户和服务器之前不必存在长期的关系。UDP 发送数据报之前也不需要经过握手创建连接的过程。
  • UDP 支持多播和广播。