原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,非公众号转载保留此声明。
少年,你在怀着非法的心态看一篇简短的硬核科普!
先抛问题:如何杀掉一个正在等待 TCP 连接的 Thread?
由于众所周知的原因,在国内使用 maven,会等待很长的时间来下载相应的 jar 包。
如果我们正在使用 IDEA,就经常容易卡住。当我们点击进度条的时候,无论是等待,还是取消,都需要等待比较长的时间来完成当前的网络请求。
除非我们立马把 IDEA 关掉,然后再重新打开它。
why?
因为你没法用代码杀掉一条处于连接状态的连接。操作系统没有有暴露这样的 API!
但你可以杀掉进程。当进程停止的时候,与之关联的所有连接都会被释放。但是你无法杀掉线程,因为线程正在 BLOCK 在某个连接之上,你需要先关掉这个连接才能让线程自动释放。
一般的连接工具包,都会提供 soTimeout 这个参数,用来配置超时。比如 MySQL 客户端:
jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000
通过设置超时时间可以防止出现网络错误时一直等待的情况并缩短故障时间,防止死连接的产生。但如果连接没有设置超时呢?
它就会永远 Block 在那里!
在 Linux 上,有 tcpkill、killcx 等工具,可以杀掉一条处于 established 状态的连接。
以tcpkill为例,我们需要安装相应的工具包。
yum install dsniff -y
然后,使用netstat 或者 ss, 或者 lsof 等命令,找到要杀掉的连接。然后杀掉它。
tcpkill -9 -i eth0 host 10.0.1.197 and port 2222
执行了这样的操作之后,Thread 就能够自动正常关闭了。
那它是怎么实现的呢?
这又和老生常谈的 TCP 四次挥手有关了。
想要关掉一条连接,需要经过 FIN 包和 ACK 包做四次挥手。这个过程很麻烦,但不要忘了,我们还有 RST 包,它可以直接引起连接的关闭。
可惜的是,如果你想要发送 RST 包,那必须首先要知道交互时所使用的 SEQ 序列号,因为乱序的数据包将会被操作系统直接丢弃。
所以,工具需要首先监听这个连接,然后获取其中的序列号。再拿着这个序列号,发起模拟的 RST 数据包。你的连接就这样断掉了。
墙,也是这么干的。
作者简介:小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。