前言
TCP是OSI七层模型里面的传输层的一个协议,传输层就干一件事,建立端到端的连接,那什么是端到端呢?比如说,客户端到服务端就是端到端

假设我用谷歌和火狐浏览器同时登录网站,如何让数据去到指定的软件服务上,就需要用到端口号作为地址来定位了。比如客户端这里生成不同的端口号,即使同时访问http端口80也是没问题的,根据不同的源端口号来做出响应就可以了,传输层在网络层的端到端基础上实现了服务进程到服务进程的传输。
其实IP地址 + 端口号Port = 套接字Socket
理解 TCP 的三次握手和四次挥手,最简单的方法是把它想象成两个人通过对讲机进行一次严谨的通话。
TCP 协议之所以复杂,是因为它要保证“可靠性”:即便网络环境很差,也要确保数据不丢、不错、不乱。
TCP 是全双工通信(双方都能同时收发)。要建立可靠连接,必须确认四个点:
- 客户端的发送能力
- 客户端的接收能力
- 服务端的发送能力
- 服务端的接收能力
三次握手 (建立连接)

TCP报文里面有SYN(同步)、ACK、FIN等标识,如果设置1就是开启这些标识,如果设置为0就是关闭这些标识。
目的: 确认双方的收发能力都是正常的,并同步初始化序列号。
以打电话为例:
- 客户端: “喂,你能听到我说话吗?” (第一次握手)
- 服务端: “听到了!你能听到我说话吗?” (第二次握手)
- 客户端: “听到了,那我们要开始正式谈话了。” (第三次握手)
详细过程:
第一次握手 (SYN=1, seq=x)
客户端发送一个 SYN 包,请求建立连接(细说就是客户端想要和服务端进行数据的同步,同步以后,也就是三次握手之后,客户端和服务端就可以互发信息)。此时客户端进入 SYN_SENT 状态。只开启SYN包是不够的,报文里面还有一个重要的字段Sequence序号(初始序列号)。
来了解一下这个seq吧
在 TCP 三次握手中,那个 seq 字段的全称是 Sequence Number(序列号),你可以把它想象成“给每一段话(每一个字节)打上的页码”,它是 TCP 实现“可靠传输”的灵魂所在。在第一次握手时,客户端发送的第一个 seq 被称为 ISN (Initial Sequence Number,初始序列号)。
特点:
- 为了安全和防止网络中残留的旧连接包干扰新连接,这个数字通常是由操作系统随机生成的
- 它是以字节为单位的计数器:TCP 会给发送的每一个字节数据都分配一个唯一的序号。
作用:
A. 确认对方收到了(可靠性)
当客户端发出 seq=x 的包时,服务端必须回一个 ack=x+1 的包。
- 这个 ack=x+1 的意思是:“我收到了你第 x 位之前的全部信息,下次请从第 x+1 位开始发。”
- 如果客户端发了包但没收到对应的 ACK,它就知道包丢了,会触发超时重传。
B. 重新排序(乱序处理)
网络传输就像寄快递,走的路不同,到达的时间也不同。
- 假设你发了三条信息:页码 1、页码 2、页码 3。
- 结果因为网络拥堵,页码 3 先到了,页码 1 最后才到。
- 接收端会根据 seq 重新排列,确保你看到的信息是“123”,而不是“321”。
C. 去除重复数据(去重)
如果网络抖动,客户端以为包丢了,又重发了一遍页码 2。
- 接收端发现:咦,页码 2 我已经收过了。
- 于是接收端会直接丢弃这个重复的包,保证数据不会变多。
补充
如果客户端每一次发来的SYN包,服务器都要记住其序号,并且生成自己需要记住的序号,那服务器就需要挂起非常多的资源。如果有黑客借此不断发送SYN包,又不进行下一步操作,服务器就会原地奔溃,这也就是典型的DDoS攻击。因此服务器不保存自己的序列号,而是根据服务器的IP地址和端口号等私有信息进行算法的运算得到序号
第二次握手 (SYN=1, ACK=1, seq=y, ack=x+1)
服务器收到后,回复一个 SYN+ACK 包。表示:我收到你的请求了(ACK),并且我也想和你建立连接(SYN)。此时服务器进入 SYN_RCVD 状态。
第三次握手 (ACK=1, seq=x+1, ack=y+1)
客户端收到服务器的包,再发一个 ACK 包(确认)。此时客户端进入 ESTABLISHED 状态,服务器收到后也进入 ESTABLISHED 状态。
三次握手中 seq 和 ack 的演变过程(通俗演示)
假设客户端随机生成的初始序列号 ISN = 100,服务端生成的 ISN = 500。
- 第一次握手(客户端 -> 服务端)
- 内容:SYN=1, seq=100
- 含义:“我要建立连接,我的初始序列号是 100。”
- 第二次握手(服务端 -> 客户端)
- 内容:SYN=1, ACK=1, seq=500, ack=101
- 含义:“收到!你给我的 100 号包我拿到了,下次请发 101 号(ack=101)。另外,我的初始序列号是 500(seq=500)。”
- 第三次握手(客户端 -> 服务端)
- 内容:ACK=1, seq=101, ack=501
- 含义:“收到!你给我的 500 号包我也拿到了,下次请发 501 号(ack=501)。你看,我现在就按你的要求发 101 号包给你了(seq=101)。”
为什么要三次?两次行不行?
不行。
如果只有两次握手,会就产生两个核心问题:无法确认客户端的接收能力 以及 产生“僵尸连接”导致服务器资源浪费。
想象一下,你给朋友寄了一封信(SYN):“我们见个面吧?”
- 场景 A(正常情况): 信很快到了,朋友回信(ACK)说:“好啊!”。你们见面了。
- 场景 B(两次握手的灾难):
- 你寄出的第一封信(SYN 1)在路上被邮差弄丢了,或者是堵车堵了大半年。
- 因为没收到回信,你以为信丢了,又寄了第二封信(SYN 2)。
- 这次很快,朋友收到 SYN 2,回信说“好啊”,你们见面,事情办完,各回各家。
- 关键点来了: 过了半年,那封堵在路上的第一封信(SYN 1)突然送到了朋友手里。
- 如果是两次握手:朋友收到 SYN 1,立刻回信“好啊!”,由于协议规定两次就成功,朋友(服务端)认为连接已经建立了,于是坐那儿傻等你的消息。
- 但此时的你(客户端)根本没想约他,你会直接无视这封莫名的回信。
- 结果: 朋友(服务器)白白浪费了时间和精力(系统资源),成了“僵尸连接”。
握手后就建立了连接,客户端就可以发送HTTP请求了,服务器响应内容。
四次挥手 (断开连接)

目的: 保证双方数据都传输完毕,安全地释放连接。
通俗版比喻:
- 客户端: “我的话说完了,我要挂了。” (第一次挥手)
- 服务端: “我知道了,等一下,我还有点话没说完。” (第二次挥手)
- (服务端继续传完剩下的数据…)
- 服务端: “好了,我也说完了,挂吧。” (第三次挥手)
- 客户端: “好的,拜拜!” (第四次挥手,然后等一会儿确认对方没再说话了,才彻底关机)
详细过程:
- 第一次挥手 (FIN):客户端发送 FIN 包,告诉服务器:“我没有数据要发了”。此时客户端进入 FIN_WAIT_1 状态。
- 第二次挥手 (ACK):服务器收到后,先回一个 ACK:“收到,但我还没准备好,请等我把剩下的数据发完”。此时服务器进入 CLOSE_WAIT 状态,客户端进入 FIN_WAIT_2 状态。
- 此时连接处于“半关闭”状态,客户端不再发数据,但仍能接收服务器发来的数据。
- 第三次挥手 (FIN):服务器数据发完了,发送 FIN 包给客户端:“我也发完了,准备断开”。此时服务器进入 LAST_ACK 状态。
- 第四次挥手 (ACK):客户端收到后回复 ACK:“收到,再见”。此时客户端进入 TIME_WAIT 状态,服务器收到 ACK 后直接 CLOSED。客户端等待 2MSL(报文最大生存时间)后也进入 CLOSED。
常见面试细节
1. 为什么挥手要四次,而握手只要三次?
- 握手时:服务器可以把 ACK(确认)和 SYN(同步)放在一个包里一起发过去。
- 挥手时:客户端说要断开,服务器可能还有数据没发完。它只能先回一个 ACK 表示收到了请求,等手头活忙完了,才能发 FIN。所以 ACK 和 FIN 是分开发送的,多了一步。
2. 为什么第四次挥手后,客户端要等 2MSL 才能彻底关闭?
有两个原因:
- 保证最后一次 ACK 到达:如果客户端发的 ACK 在路上丢了,服务器没收到,就会重发第三次的 FIN。客户端等在那,就是为了如果服务器重发了 FIN,它能再补发一个 ACK。
- 清理残留包:防止本次连接的“过期包”留在网络中,干扰下一个使用相同端口的新连接。
3. 什么是 TIME_WAIT 状态?
这是客户端在第四次挥手后的状态。如果服务器压力很大,产生大量 TIME_WAIT,可能会导致端口被占满,无法建立新连接。
总结
这里介绍了TCP三次握手和四次挥手的详细过程,以及为什么握手要三次,挥手要四次等问题。希望这篇介绍 TCP三次握手和四次挥手 对你有帮助~

