TCP 建链(三次握手)和断链(四次握手)

TCP 建链(三次握手)和断链(四次挥手)

背景

随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内容并非完全原创,大多是参考其他文章资料整理所得,感谢每位技术人的开源精神。

简介

本文介绍 TCP 的建链和断链过程,即通常所说的三次握手和四次挥手的过程。

建链(三次握手)

TCP 建链需要客户端与服务器之间交互 3 个数据包,主要作用就是为了确认双方的接收和发送能力是否正常,初始序列号、交换窗口大小以及 MSS 等信息。

  • 第一次握手

    客户端发送请求连接数据包到服务器,标志位 SYN 置为 1,随机生成一个初始序号 seq ,即 SYN=1 seq=x,客户端从 CLOSED 状态进入 SYN_SENT 状态,等待服务器回复。

  • 第二次握手

    服务器收到请求数据包后根据标志位 SYN=1 知道客户端在请求建立连接,服务器将标志位 SYNACK 都置为 1,确认序号 ack=x+1,随机生成一个初始序号 seq=y,并将该数据包 SYN=1, ACK=1, seq=y, ack=x+1 回复给客户端确认连接请求,服务器从 LISTEN 状态进入 SYN_RCVD 状态。

  • 第三次握手

    客户端收到确认后检查确认序号 ack 是否为 x+1ACK 是否为 1,如果正确则将标志位 ACK 置为1,确认序号 ack=y+1,并将该数据包 ACK=1, seq=x+1, ack=y+1 发送给服务器,发送完成后客户端进入 ESTABLISHED 状态,服务器接收到后检查确认序号 ack 是否为 y+1,如果正确则连接建立成功,服务器进入 ESTABLISHED 状态。随后客户端与服务器间可以开始传输数据。

断链(四次挥手)

  • 第一次挥手

    客户端发起 FINFIN=1, ACK=1, seq=u, ack=v,客户端从 ESTABLISHED 状态进入 FIN_WAIT_1 状态。TCP 协议规定,即使 FIN 包不携带数据,也要消耗一个序号。此 FIN 包中 ACK=1ack=v 基于断链前正常通信的数据包。

  • 第二次挥手

    服务器收到 FIN 包,发出 ACK 确认包,并带上自己的序号 seq=vACK=1 seq=v ack=u+1),服务器进入从 ESTABLISHED 状态进入 CLOSE_WAIT 状态。此时客户端已经没有数据需要发送给服务器了,但服务器如果仍有数据发送给客户端的话,客户端依然需要接收。客户端接收到服务器发送的 ACK 后进入 FIN_WAIT_2 状态。

  • 第三次挥手

    服务器数据发送完成后向客户端发送 FINFIN=1, ACK=1, seq=w, ack=u+1,半连接状态下服务器可能又发送了一些数据,假设发送 seqw,服务器进入 LAST_ACK 状态。

  • 第四次挥手

    客户端接收到服务器的 FIN 包后发出确认包 ACK=1, seq=u+1, ack=w+1,客户端进入 TIME_WAIT 状态,此时 TCP 连接还没有释放,必须经过 2*MSL 后才进入 CLOSED 状态,而服务器接收到客户端的 ACK 后就进入了 CLOSED 状态,服务器结束 TCP 连接的时间要比客户端早一些。

序号及标志位

TCP 建链(三次握手)和断链(四次挥手)中涉及到几个关键概念字段:

  • 标志位:共有 6 个,分别是:
    • ACK:确认序号有效。
    • FIN:释放一个连接。
    • PSH:接收方应该尽快将这个报文交给应用层。
    • RST:重置连接。
    • SYN:发起一个新连接。
    • URG:紧急指针(urgent pointer)有效。
  • seq:Seq 序号,占 32 位,用来标识从 TCP 源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  • ack:Ack 序号,占 32 位,只有 ACK 标志位等于 1 时此序号字段才有效,确认方 ack 等于发起方 seq + 1注意:ACKack 是两个不同的概念,不要混淆了。

延伸问题

为什么建立连接需要握手三次,两次行不行?

  • 原因一:TCP连接建立前需要确认客户端和服务器双方的收发包能力。
    • 第一次握手可以让服务器知道客户端的发送能力是正常的。
    • 第二次握手可以让客户端知道服务器的接收和发送能力都是正常的。
    • 第三次握手可以让服务器指导客户端的接收能力是正常的。
  • 原因二:确保序列号可靠同步。
    第二次握手服务器向客户端发送了自己的初始序列号,如果第二次握手报文丢失则客户端就无法知道服务器的初始序列号,所以需要第三次握手让服务器知道客户端已确认服务器的初始序列号。
  • 原因三:阻止重复历史连接的初始化。
    客户端由于某种原因发送了两个不同序号的 SYN 包,因为复杂的网络环境中旧的数据包有可能先到达服务器,如果是两次握手则服务器收到旧的 SYN 包就会立刻建立连接,从而造成网络异常。如果是三次握手,服务器需要回复 SYN+ACK 包,客户端会对比应答的序号,如果发现是旧的报文就会给服务器发 RST 包,直至正确的 SYN 包到达服务器后才正常建立连接。
  • 原因四:安全问题。
    TCP 新建连接时内核会为连接分配一系列内存资源,如果采用两次握手可能会放大DDOS 攻击。

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

第一次握手和第二次握手不可以携带数据,第三次握手可以携带数据。假如第一次握手携带数据,如果碰到恶意攻击,那么每次在第一次握手的 SYN 报文中都会加入大量数据,会造成服务器花费大量存储空间来缓存这些数据。

为什么释放连接是四次,比建立连接多一次?

建立连接时服务器的 SYNACK 是合并发送的,而因 TCP 是全双工通信,释放连接过程中在客户端发送 FIN 包后,服务器可能还有数据需要发送,不能立即关闭连接,所以不能同时发送 FIN 包和 ACK 包,只能先确认 ACK,然后等服务器无数据发送时再发送 FIN 包。

为什么 TIME_WAIT 状态需要经过 2MSL 才能返回到 CLOSED 状态?

MSL 是指报文在网络中的最大生存时间。

  • 原因一:在客户端回复服务器 FIN 包的确认包 ACK 后,这个 ACK 包可能是不可达的,如果服务器收不到 ACK 的话需要重新发送 FIN 包。所以客户端发送 ACK 后需要留出 2MSL 时间(ACK 到达服务器的时间 + 服务器重发 FIN 包时间),如果客户端等到 2MSL 后没有收到服务器重传的 FIN 包,说明可以确认服务器已经收到了客户端发送的 ACK 包。
  • 原因二:客户端发送完最后一个 ACK 包后,再经过 2MSL 时间就可以使当前连接持续的时间内所产生的所有报文都从网络中小时,使下一个新的连接中不会出现这种旧的连接请求报文。
相关推荐
自律的kkk5 天前
网络编程中的黏包和半包问题
java·开发语言·网络·网络编程·tcp·nio
运维小文6 天前
TCP协议简单分析和握手挥手过程
服务器·网络·网络协议·tcp/ip·三次握手
码农爱java8 天前
什么是 UDP 协议?UDP 协议和 TCP 协议的区别是什么?
网络协议·tcp/ip·计算机网络·udp·tcp·tcp 和 udp
EterNity_TiMe_10 天前
【Linux网络】网络基础:传输层TCP协议(二)
linux·运维·网络·udp·tcp
ミカミミミ14 天前
网络编程相关 API 学习
网络·udp·socket·tcp
乌啼霜满天24917 天前
TCP与UDP
网络协议·tcp/ip·udp·tcp
学习溢出18 天前
深入理解 TCP 标志位(TCP Flags)
网络·网络协议·tcp/ip·安全·tcp
Desire.98423 天前
计算机网络——可靠数据传输原理
网络·计算机网络·tcp
沥川同学1 个月前
跨平台应用开发框架(1)----Qt(组件篇)
c++·qt·udp·线程·tcp·qt5·qt6.3
ZachOn1y1 个月前
计算机网络:运输层 —— TCP 的超时重传机制
网络·网络协议·tcp/ip·计算机网络·tcp·超时重传