TCP三次握手和四次挥手
客户端ip:192.168.9.117,服务端ip:47.103.68.136
http请求:47.103.68.136:300

(1)三次握手

这里可以观察到的现象,http请求是服务端断开的连接,而不是常见socket通信中客户端请求断开连接。
(2)四次挥手

看资料,说要客户端断开连接,可以设置connection:close,


但测试发现,仍然是服务端先断开连接的,只是服务端不是发送单独的Fin,ACK,而是同数据一起发送的。
为什么是服务器主动断开?
短连接的核心思想是:
-
服务器控制连接的生命周期:
-
服务器知道自己是否还有数据要发送,如果不需要保持连接,就可以直接
close(socket)。 -
客户端不能随意关闭连接,否则可能会导致数据未完全接收。
-
-
服务器节省资源:
-
服务器通常要处理多个请求,主动关闭连接可以释放 socket 资源,防止客户端长时间占用。
-
-
HTTP 1.0 及早期 HTTP 1.1 实现:
-
HTTP/1.0 默认是短连接,每个请求-响应对都会新建并关闭一个 TCP 连接。
-
HTTP/1.1 默认是 长连接(Keep-Alive),但如果服务器不希望保持连接,它会 在响应头中加
Connection: close,然后主动关闭
-

使用http1.1确实可以看到建立长连接,由客户端控制关闭,但事实上只看到了三次挥手,经查证,它是四次挥手的优化版本,它符合 TCP 协议的标准行为之一。

按照标准,服务端的FIN是分开发的
Client → Server: [FIN, ACK] (请求关闭)
Server → Client: [ACK] (确认)
Server → Client: [FIN, ACK] (服务器也关闭)
Client → Server: [ACK] (确认)
是合法的! 服务器可以选择:
-
先
ACK客户端的FIN,稍后再FIN -
或者直接
ACK + FIN一起发送
这种 合并 ACK 和 FIN 的方式 叫做 "优化的四次挥手",TCP 允许这种行为。服务器这么做的目的是减少一次网络往返,提高效率。
如果服务器没有额外的数据要发送,它就可以立即 FIN。
1.为什么TCP会有四次挥手?(截图可以看出来,资源是分开返回的,返回html,再加载图片等资源)
tcp是全双工模式,即客户端和服务端能同时发送数据,所以每个方向的数据需要单独确认,主要是因为 TCP 允许双向数据传输是独立的,关闭一个方向的传输不应该影响另一个方向的正常通信,比如客户端请求关闭连接了,表示客户端不再发送数据,但不表示服务端不能再发送数据,服务端可以发送数据,客户端也可以接收数据。而关闭连接是双方都不再发送数据。所以客户端告诉服务端不再发送数据了,服务端确认,但服务端还是可以发送数据的,直到服务端发送fin,告诉客户端自己不再发送数据,客户端确认,才能确认连接关闭。
为什么不能客户端关闭,服务端就关闭?
服务器的数据还没完全发完,但连接已经关闭,客户端收不到数据,造成数据丢失。
四次挥手确保:
-
客户端
FIN只影响自己,不影响服务器继续发数据。 -
服务器
FIN只在数据完全发送完毕后才触发,确保所有数据完整传输。四次挥手确保服务器能在收到FIN后,继续发送剩余数据,直到完全发送完毕,才正式关闭连接。

客户端发起FIN,服务端ACK

2.为什么服务器 ACK 和 FIN 可能会合并?
-
服务器不需要再等数据:如果服务器 没有额外数据需要发送,它可以直接
FIN关闭连接。 -
减少 TCP 交互次数:合并
ACK和FIN可以 减少一次往返,提高效率。 -
操作系统 TCP 栈优化:某些系统为了减少
CLOSE_WAIT可能会自动合并ACK + FIN。
3.为什么会有 TIME_WAIT 状态?
关键点:避免“旧连接数据”影响新连接
如何做到的?进入TIME_WAIT状态,前的连接无法立即被重新使用。最大报文生存时间,当前TCP数据包可能在网络中留存的时间,该时间范围内,数据包被丢弃。
在 TCP 四次挥手后,主动关闭连接的一方(通常是客户端)会进入 TIME_WAIT 状态,通常会 持续 2 * MSL(最大报文生存时间),一般是 30-120 秒。
TIME_WAIT 的作用
a.防止延迟数据干扰新连接
-
-
如果网络中存在延迟的 TCP 数据包,而新连接刚好复用了相同的
IP:Port,这些 “旧数据” 可能被错误地认为是新连接的数据,导致数据错乱。 -
TIME_WAIT让旧数据有足够时间被丢弃,保证新连接的数据不会受干扰。
-
b.确保对方正确接收到 ACK
-
-
在 TCP 最后一个
ACK发送后,如果客户端 立刻释放连接,但这个ACK丢失了,服务器会 重发FIN,但客户端已经关闭,导致服务器 一直等待。 -
TIME_WAIT确保客户端可以重发ACK,确保服务器顺利关闭连接。
-
根据上面的测试,http服务器通常是主动关闭连接的一方,http1.0协议大多都是短连接协议,这就可能导致:
- 在高并发服务器上,短连接频繁创建和关闭,会导致大量
TIME_WAIT连接: -
端口资源被占用,服务器可能 无法创建新连接(
port exhaustion)。
系统性能下降,太多 TIME_WAIT 连接会占用内存和 CPU。

解决方案
为了避免 TIME_WAIT 状态占用过多资源,有一些方法可以优化:
- 调整
TIME_WAIT的持续时间
在 Linux 上,你可以通过调整内核参数来减小 TIME_WAIT 的持续时间:
sysctl -w net.ipv4.tcp_fin_timeout=30
这将减少 TIME_WAIT 的持续时间。
- 启用端口重用(
SO_REUSEADDR)
某些操作系统允许在 TIME_WAIT 状态时重用端口(例如,Linux 系统中通过设置 SO_REUSEADDR 选项)
- 使用长连接(Keep-Alive)
对于需要频繁通信的应用,使用 TCP 长连接而不是每次都创建新的短连接,可以显著减少 TIME_WAIT 状态。
4.CLOSE_WAIT状态
是 TCP 协议中的一个连接状态,表示一方已经收到关闭连接的请求(FIN),并且已经准备好关闭连接,但还没有关闭自己的数据流(即没有发送 FIN 包)。换句话说,CLOSE_WAIT 是连接的一方等待关闭连接的状态。
当 TCP 连接的一方收到另一方的 FIN(关闭连接的请求)时,该方会进入 CLOSE_WAIT 状态。这个状态表示:
- 另一方已经请求关闭连接(发送
FIN)。
- 当前这方还没有关闭连接,即还没有发送自己的
FIN来完成连接的断开。
a.客户端关闭请求: 发送 FIN 请求关闭连接,进入 FIN_WAIT_1 状态。
b.服务端收到 FIN 请求: 服务端会返回一个 ACK 来确认收到 FIN,并进入 CLOSE_WAIT 状态。
c.服务端: 在 CLOSE_WAIT 状态下,服务端应当在完成必要的数据处理后(比如应用层数据传输)发送自己的 FIN 来关闭连接。
如果一方长时间处于 CLOSE_WAIT 状态,可能意味着该方没有正确关闭连接。
-
系统资源可能会泄漏,因为即使连接已经关闭,但连接的套接字资源仍然被占用,无法释放,导致无法回收端口。
-
TCP 连接可能会积压大量的
CLOSE_WAIT连接,这会导致服务器的资源耗尽,影响正常服务。
状态的常见原因
- 应用程序没有及时关闭连接:
如果服务器端的应用程序没有处理完所有数据后立即关闭连接,CLOSE_WAIT 状态将持续存在。通常,程序需要在收到 FIN 后,尽早调用 close() 或 shutdown() 来关闭连接。
- 资源未释放:
如果某个程序无法及时释放资源,或存在 bug 导致它没有正确关闭连接,也会导致大量 CLOSE_WAIT 状态。
解决方案
- 检查应用层代码:
确保应用程序在收到 FIN 后,及时调用 close() 或 shutdown() 来关闭连接。通常,在应用层处理完数据后,必须关闭连接。
在 Linux 系统上,你可以使用 lsof 或 netstat 命令来查看哪些应用程序导致了 CLOSE_WAIT 状态:lsof -i :<port> | grep CLOSE_WAIT netstat -anp | grep CLOSE_WAIT
- 优化服务器应用程序:
-
优化连接管理:
-
使用长连接(如 HTTP Keep-Alive)减少连接的建立和关闭次数。
-
如果可能,使用 连接池 等技术,重用连接而不是频繁地打开和关闭连接。
-
- 增加操作系统的连接关闭超时设置:
一些操作系统允许你调整连接关闭的超时设置,例如在 Linux 上使用 sysctl 配置:
其他的TCP问题:
问题1:SYN Flood 攻击防护
-
TCP 三次握手中,服务器收到
SYN但未收到ACK,会进入SYN_RECV状态。 -
攻击者可以伪造大量
SYN请求,导致服务器SYN_RECV过多,拒绝新连接。
解决方案:
-
启用 SYN Cookies(Linux 默认开启)
-
限制半连接队列大小
-
使用防火墙(iptables)限制异常连接
问题2:TCP 拥塞控制优化
-
在高流量环境下,TCP 拥塞控制会影响带宽利用率,常见算法包括:
-
Reno(经典拥塞控制)
-
Cubic(Linux 默认,适合高带宽)
-
BBR(Google 开发,低延迟高吞吐)
-
优化方案:
-
切换到 BBR 拥塞控制算法(适用于高并发、大带宽场景)
问题3:TCP 延迟 ACK 影响实时性
-
默认情况下,TCP 为了减少小数据包数量,会合并多个 ACK 进行延迟发送。
-
在某些场景(如低延迟应用、金融交易),这种延迟会影响性能。
解决方案:
-
关闭 TCP 延迟 ACK(慎用,可能增加小包流量)
问题4:TCP 半连接 & 半关闭
-
在某些应用场景下,可能只需要单向关闭连接,例如:
-
HTTP 服务器 关闭输入流,但继续发送响应。
-
文件传输 一方发送完数据后,只关闭写端,保留读端。
-
解决方案:
-
使用
shutdown()进行单向关闭:
问题5:TCP RST 断开连接
-
在某些情况下,TCP 连接可能直接被
RST(重置)断开,导致连接异常:-
服务器端口未监听,直接
RST返回。 -
应用层超时,主动调用
setsockopt()关闭连接。 -
服务器进程崩溃,导致 TCP 连接直接
RST终止。
-
排查方法:
-
使用
tcpdump捕获RST包: -
检查应用程序是否使用
SO_LINGER:
问题6:负载均衡 & TCP 会话保持
-
在多台服务器之间做 负载均衡(如 Nginx, LVS) 时,TCP 连接可能在多个后端之间跳转,导致会话丢失。
解决方案:
-
基于 IP 或 Cookie 绑定连接
-
使用 TCP 直连模式(Nginx
proxy_pass+keepalive) -
使用 QUIC(HTTP/3),避免 TCP 连接重建

浙公网安备 33010602011771号