TCP四次挥手流程
用中文来描述下这个过程:
Client(可以是客户端,也可以是服务器端): 服务端大哥,我事情都干完了,准备撤了
,这里对应的就是客户端发了一个FIN,然后进入 FIN-WAIT-1 状态
Server:知道了,但是你等等我,我还要收收尾
,这里对应的就是服务端收到 FIN 后回应的 ACK
经过上面两步之后,服务端就会处于 CLOSE_WAIT 状态,客户端处于 FIN-WAIT-2 状态。过了一段时间 Server 收尾完了
Server:小弟,哥哥我做完了,撤吧
,服务端发送了FIN,然后进入 LAST-ACK 状态
Client:大哥,再见啊
,这里是客户端对服务端的一个 ACK,然后进入 TIME-WAIT 状态
Server:再见,我跑路了,服务端收到 ACK ,然后进入 CLOSED 状态
Client:等待中...
,必须等待 2MSL 个时间,然后进入 CLOSED 状态
为什么要四次挥手
TCP连接是双向传输的对等的模式(全双工模式),就是说双方都可以同时向对方发送或接收数据。这就意味着:
当 Client 发出FIN报文段时,只是表示 Client 已经没有数据要发送了,Client 告诉 Server,它的数据已经全部发送完毕了;但是,这个时候 Client 还是可以接受来自 Server 的数据;
当 Server 返回ACK报文段时,表示它已经知道 Client 没有数据发送了,但是 Server 还是可以发送数据到 Client 的;
当 Server 也发送了FIN报文段时,这个时候就表示 Server 也没有数据要发送了,就会告诉 Client ,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
得出的结论:
当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了,这时对方会回一个ACK,此时一个方向的连接关闭。
但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个FIN段来关闭此方向上的连接,接收方发送ACK确认关闭连接。
注意,接收到FIN报文的一方只能回复一个ACK, 它是无法马上返回对方一个FIN报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”
。
重要状态
1. CLOSE_WAIT
- CLOSE_WAIT出现时机与原因
- 代码问题:请求的时候没有显式关闭 Socket 连接,或者死循环导致关闭连接的代码没有执行到,即 FIN 包没有发出,导致 CLOSE_WAIT 不断累积
- 响应过慢 / 超时设置过小:双方连接不稳定,一方 Timeout,另外一方还在处理逻辑,导致 Close 被延后
- CLOSE_WAIT排查方法
1、查看网络连接
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
查看 TCP 连接中 CLOSE_WAIT 的数量可以使用以下命令:
netstat -antop | grep CLOSE_WAIT | wc -l
通过以上命令可以看到服务器上的 CLOSE_WAIT 将近 1w2,这是个不容小觑的潜在问题。
2、梳理 TCP 连接流向
接着我们可以从上图梳理出 CLOSE_WAIT 的连接流向,命令返回里中的 Foreign Address(第 5 栏)代表对方的 IP 地址,即和我们连接着但是却主动关闭了连接的机器。
3、根据项目数据请求流向还原可能场景
然后我们可以根据项目数据请求流向,还原出可能的场景,在我这里即是 CLOSE_WAIT 都发生在本机爬虫、代理以及目标网站的连接上。
毕竟 Program name(最后一栏)都写着 Python 了,且这台服务器上只有爬虫用的 Python。
- CLOSE_WAIT解决思路
..................
2. TIME_WAIT
- TIME_WAIT出现原因与查看
出现时机:
TIME_WAIT永远是出现在主动发送断开连接请求的一方(下文中我们称之为客户端)。
等待2ML原因:
排查方法:
(1)ss -tan state time-wait|wc -l (2)netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
- TIME_WAIT的危害
1.占用连接资源
2.占用内存资源
这个占用资源并不是很多,可以不用担心。
- TIME_WAIT的解决
可以考虑如下方式:
1.增加可用端口范围(修改net.ipv4.ip_local_port_range); 增加服务端口,
比如采用80,81等多个端口提供服务; 增加客户端ip(适用于负载均衡,比如nginx,采用多个ip连接后端服务器); 增加服务端ip; 这些方式治标不治本,只能缓解问题。
2.将net.ipv4.tcp_max_tw_buckets设置为很小的值(默认是18000). 当TIME_WAIT连接数量达到给定的值时,所有的TIME_WAIT连接会被立刻清除,并打印警告信息。
但这种粗暴的清理掉所有的连接,意味着有些连接并没有成功等待2MSL,就会造成通讯异常。
3.修改TCP_TIMEWAIT_LEN值,减少等待时间,但这个需要修改内核并重新编译。
4.打开tcp_tw_recycle和tcp_timestamps选项。
5.打开tcp_tw_reuse和tcp_timestamps选项。
- https://juejin.cn/post/6844903734300901390
- https://juejin.cn/post/6844903730874171405