TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP 报文#

在讲解之前先了解下 TCP 报文的格式,上图是报文的首部,这里不需要了解它全部字段的作用,只需要了解 3 个字段即可。
-
序号“sequence number”:下文简称 seq,占用 4 个字节。
-
确认号“acknowledgement number”:下文简称 ack,占用 4 个字节,用于确认序号,并返回序号+1。如序号是 1626544836,则确认号是 1626544837。
-
标志域“tag flags”:如图所示有 6 位,这里只用到其中 3 位,分别是:
- SYN(SYNchronization):同步标志。
- ACK(Acknowledgement):确认标志。
- FIN(Finis):终结标志。
当其中哪个标志有效时,它的值为 1。 如
SYN=1时,表示标志域中 SYN 标志生效,这段报文也可以叫做“ SYN 报文”或者“同步报文”。
三次握手#
过程#

第一次握手:
客户端发送(SYN=1,seq=J)的同步报文给服务器,然后进入SYN_SEND状态,等待服务器发回确认报文;
第二次握手:
服务器收到 SYN 报文后,如果同意连接,则发送(ACK=1,ack=J+1,SYN=1,seq=K)SYN-ACK 报文给客户端,
然后进入SYN_RECV状态,等待客户端发回确认报文。
第三次握手:
客户端收到服务器的 SYN-ACK 报文后,还需要向服务器给出确认,发送一个(ACK=1,ack=K+1)确认报文给服务器,然后进入ESTABLISHED状态。
服务器收到客户端的确认报文后,也会进入ESTABLISHED状态。此时,TCP 连接建立成功。
过程简析#
用大白话讲就是
- 第一次握手:客户端对服务器说:“喂,你能听到我说的话吗?”
- 第二次握手:服务器回答客户端说:“嗯,我能听到,那你能听到我说的话吗?”
- 第三次握手:客户端回答服务器说:“嗯,我也能听到了!”然后,客户端继续对服务器说:“好,现在你听我说...”, 然后就是 BALABALA 一堆话了(HTTP 请求)。
第三次握手后,客户端就可以对服务器发起 HTTP 请求了。
说明:TCP 协议是传输层协议,HTTP 是应用层协议,HTTP 协议是建立在 TCP 协议上的。还有个 IP 协议是网络层协议。TCP 协议建立在 IP 协议上。
OSI 模型把网络通信分为七层,层层递进。
问题解答#
为什么是三次握手而不是二次握手呢?
是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
假设二次握手成立,那么存在下面几种情况:
-
情况一,客户端对服务器发起请求,服务器接收到请求后,发送确认建立连接的信息,连接建立完成。
-
情况二,客户端对服务器发起请求,但是这个请求突然丢失了,服务器没有收到。之后,客户端又发送一条请求,服务器这次接收到请求了,发送确认建立连接的信息,连接建立完成。
-
情况三,客户端对服务器发起请求,这个请求没有丢失,它在某个网络节点处滞留了,过了很久后,服务器才收到这个其实已经失效了的请求,但是服务器不知道这个请求失效了,还是向客户端发送确认建立连接的信息,于是连接建立完成。但是客户端现在并不需要发送请求啊,于是就不理睬服务器已经建立起来的连接,也不会向服务器发送任何请求数据。服务器就一直在等呀等,等着客户端发送请求数据过来,然而客户端现在压根就不想鸟他,因此服务器浪费了很多的资源。
如果是三次握手的情况下,就不会存在情况三了。在服务器发出连接确认的信息后,
如果没有收到客户端的确认信息,就不会建立连接了,不会傻傻的等着,浪费资源了。
半连接队列#
通过上面分析,我们知道三次握手的必要性,需要确认两次才会建立连接。
其实,在服务器收到客户端 SYN 报文后,会维护一个半连接队列,该队列为每个客户端的 SYN 包(syn=j)开设一个条目,该条目表明服务器已收到 SYN 报文,并向客户端发出确认,正在等待客户端的 ACK 报文。
如果等待一段时间后没有收到客户段的 ACK 报文,服务器会重新发送 SYN-ACK 报文给客户端,然后继续等待着。如果等待一段时候后还是没收到客户端的 ACK 报文,服务器会进行第二次重传 SYN-ACK 报文给客户端...
如此循环,注意:每次等待的时候不一定相同。直到达到服务器设置的最大重传次数之后,服务器会将连接信息从半连接队列中删除掉。这样的连接信息也叫做“半连接请求”。
SYN 攻击#
SYN 攻击利用 TCP 协议漏洞,通过发送大量半连接请求,耗费服务器 CPU 和内存资源。
通常,SYN 攻击者会在短时间内大量伪造不存在的 IP 地址,然后向服务器发送 SYN 包,服务器回复确认包,并等待客户端回复。但是源地址不存在,不可能回复服务器,服务器需要不停重发确认包直至超时,这些伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
四次挥手#
过程#
因为不管是客户端还是服务端,都可以发起挥手动作,所以下面以主机 A 和主机 B 区分。

第一次挥手:
主机 A 向主机 B 发送(FIN=1,seq=M)FIN 报文,请求关闭连接,然后进入FIN_WAIT_1状态。
第二次挥手:
主机 B 收到 FIN 报文并确认后,向主机 A 发送(ACK=1,ack=M+1)ACK 报文,表示同意主机 A,这时,主机 B 进入CLOSE_WAIT状态。主机 A 收到 ACK 报文后,进入FIN_WAIT_2状态。
第三次挥手:
主机 B 也向发送(FIN=1,seq=N)FIN 报文,也请求关闭连接,然后进入LAST_ACK状态。
第四次挥手:
主机 A 收到 FIN 报文确认后,向主机 B 发送(ACK=1,ack=N+1)ACK 报文,表示同意主机 B,然后进入TIME_WAIT状态,经过 2MSL 之后,自动进入CLOSED状态,四次挥手结束。主机 B 收到 ACK 报文后,也进入CLOSED状态。
说明:MSL“Maximum Segment Lifetime”(最长报文段寿命),根据 RFC 793 建议该值为 2 分钟。
过程简析#
用大白话讲就是:
- 第一次挥手:主机 A 对主机 B 说:“喂,兄弟!我这边没有数据要给你了,我们关闭连接吧”
- 第二次挥手:主机 B 回答主机 A 说:“嗯,了解,我现在也不需要你的数据了。不过我要先看看我这边还有没有数据要发送给你”。主机 A 回答主机 B 说:“收到”
- 第三次挥手:主机 B 又对主机 A 说:“嗯,我这边也没有数据要给你了,我们关闭连接吧”
- 第四次挥手:主机 A 回答主机 B 说:“好勒!同意!”。主机 B 听到主机 A 的回答后,话都没说,直接就把连接给关闭了。
主机 A 没有听到主机 B 说话,有点不放心,等了一段时间后(4 分钟),才把连接关闭了。
问题解答#
为什么握手时三次,而挥手却是四次呢?
握手的时候,服务器收到 SYN 报文后,服务器在 LISTEN 状态下,可以直接一次性发送 ACK+SYN 报文出去。
因为 TCP 连接是全双工的(可以同时发送和接收),因此在关闭连接的时候,需要关闭发送和接受两个接口在能关闭连接。
在挥手的时候,发出 FIN 的主机 A 只是表示没有数据发送了,但是它还可以接受数据,所以不能马上关闭连接,要等到收到 FIN 的主机 B 确认也没有数据传出去的时候,才能关闭连接。通俗一点来讲,就是:主机 B 确认自己已经不需要主机 A 的数据后,发生 ACK 报文给主机 A,告诉主机 A,我不用接受你的东西了,你可以关闭发送接口了。
然后再确认自己所有的东西都已发送完毕,又发送 FIN 报文给主机 A,告诉主机 A,我也没有东西要发送给你了,你可以关闭接收接口了(此时,主机也想要关闭发送接口)
综上所述,挥手需要四次。
为什么主机 A 第四次挥手的时候,需要经过 2MSL 后,才关闭接口?
因为网络是不可靠的,主机 A 第四次挥手发送的 ACK 报文无法保证主机 B 一定能收到,有可能丢失,所以主机 A 发送 ACK 报文后,进入TIME_WAIT的状态,这个状态的作用就是用来重发可能丢失的 ACK 报文。