LINUX: 在不重启各自socket程序情况下, 断开ESTAB的TCP链接

2023-11-06

一说起TCP, 就是什么三次握手, 四次挥手. 而这次想讨论的是:

在不重启各自socket程序情况下, 将ESTABLED链接断开 ???

情景模拟

简单点, 在同一个机器 通过 nc 来实现 serverclient

# Server
nc -l -p 5555
# Client
nc localhost 5555 -p 6666

上面的意思就是, server端在5555端口监听, 而client 通过 6666 端口去连接

为了更加清晰的看到流量, 咱们通过 tcpdump 来观察:

tcpdump -i lo  -xnn -S  # 因为是本机, 所以lo才能捕获
08:32:01.063394 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [S], seq 1812097880, win 43690, options [mss 65495,sackOK,TS val 2762998 ecr 2761788,nop,wscale 7], length 0
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  003c 43ae 4000 4006 f90b 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 6c02 6b58 0000 0000 a002
    0x0030:  aaaa fe30 0000 0204 ffd7 0402 080a 002a
    0x0040:  28f6 002a 243c 0103 0307
08:32:01.063416 IP 127.0.0.1.5555 > 127.0.0.1.6666: Flags [S.], seq 1320008227, ack 1812097881, win 43690, options [mss 65495,sackOK,TS val 2762998 ecr 2762998,nop,wscale 7], length 0
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  003c 0000 4000 4006 3cba 7f00 0001 7f00
    0x0020:  0001 15b3 1a0a 4ead ba23 6c02 6b59 a012
    0x0030:  aaaa fe30 0000 0204 ffd7 0402 080a 002a
    0x0040:  28f6 002a 28f6 0103 0307
08:32:01.063431 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [.], ack 1320008228, win 342, options [nop,nop,TS val 2762998 ecr 2762998], length 0
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0034 43af 4000 4006 f912 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 6c02 6b59 4ead ba24 8010
    0x0030:  0156 fe28 0000 0101 080a 002a 28f6 002a
    0x0040:  28f6

ss的结果也证明了链接已经建立了:

[root@5464f8622628 /]# ss -ant
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
ESTAB      0      0      172.17.0.3:6666               172.17.0.3:5555
ESTAB      0      0      172.17.0.3:5555               172.17.0.3:6666

链接建立之后, 就能互相通信了
请在这里输入图片描述

那么如何断开这个链接呢?

错误姿势

现在来试下传统方法, 一般我们会上iptables:

[root@6913388a8a1e /]# iptables -A INPUT -p tcp --dport 5555 -j DROP
[root@6913388a8a1e /]# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       tcp  --  anywhere             anywhere             tcp dpt:personal-agent

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

上面的规则, 意思就是将 目的端口为 5555 的请求丢弃了, 所以我们必须从客户端发信息, 因为客户端才能发到5555端口:
请在这里输入图片描述

这里已经看到, 信息已经发不出去了, 而通过tcpdump能看到一堆从client发送的报文:

08:43:44.459584 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2833338 ecr 2832362], length 2
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0036 75f8 4000 4006 c6c7 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 138b 421d d4ae c000 8018
    0x0030:  0156 fe2a 0000 0101 080a 002b 3bba 002b
    0x0040:  37ea 330a
08:43:44.670096 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2833359 ecr 2832362], length 2
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0036 75f9 4000 4006 c6c6 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 138b 421d d4ae c000 8018
    0x0030:  0156 fe2a 0000 0101 080a 002b 3bcf 002b
    0x0040:  37ea 330a
08:43:44.881782 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2833380 ecr 2832362], length 2
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0036 75fa 4000 4006 c6c5 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 138b 421d d4ae c000 8018
    0x0030:  0156 fe2a 0000 0101 080a 002b 3be4 002b
    0x0040:  37ea 330a
.... (剩下还有大概 8 条左右)

tcpdump的输出告诉我们client真的已经在努力了, 但是server却不响应, 这真不怪server绝情, 而是它真的没有收到! 都被那可恶的iptables丢掉了.!

client会因为server不搭理而情绪低落放弃它们的连接么?

[root@6913388a8a1e /]# ss -ant
State       Recv-Q Send-Q  Local Address:Port                 Peer Address:Port
ESTAB       0      2           127.0.0.1:6666                    127.0.0.1:5555
ESTAB       0      0           127.0.0.1:5555                    127.0.0.1:6666

很明显, 它们之间是真爱, 尽管server不搭理, client也不会轻易放弃.

而且很有意思的是, tcpdump还在持续的输出:

.....(省略贼多的信息)
08:53:28.844326 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2891776 ecr 2832362], length 2
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0036 7606 4000 4006 c6b9 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 138b 421d d4ae c000 8018
    0x0030:  0156 fe2a 0000 0101 080a 002c 2000 002b
    0x0040:  37ea 330a
08:55:31.721921 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2904064 ecr 2832362], length 2
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0036 7607 4000 4006 c6b8 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 138b 421d d4ae c000 8018
    0x0030:  0156 fe2a 0000 0101 080a 002c 5000 002b
    0x0040:  37ea 330a

比较细心的同学, 可能就会发现, 它们通信的时间, 在不断的增加, 从一开始几毫秒, 到现在的2分钟, 这是由TCP协议中的RTTRTO所决定的.

RTT (round trip time)
在开启了TCP时间戳后,A记录下时间t1把包发给B,B收到包后记录下时间t2把包回给A ,这个过程里t2-t1就是RTT

RTO(Retransmission TimeOut)即重传超时时间

所以为了节省性能, client重试的时间, 会随着这套算法, 不断的增加~ 但是他们的链接, 已经会长存...至于长存多久, 这个真的取决很多因素, 例如keepalived保活机制等等, 在这里, nc大概13分钟就看不下去了...

那么假设, 还没到那么长时间 而且 iptables良心发现了, 放弃了阻扰, 它们又会怎样呢?

[root@6913388a8a1e /]# iptables -F
[root@6913388a8a1e /]# iptables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

在下次RTO结束时, server就能接收到相应的信息了, 从此clientServer又能愉快的玩耍了

花了很大的篇幅来证明clientserver的真爱, 事实证明它们的专一值得学习!

但是很多时候, 如果clientserver冷战, 谁也不理谁, 这就让我们很蛋疼了, 因为如果这样不必要的链接, 长时间保存, 会大量的占用资源, 很快就会出现资源瓶颈, 所以我们一定要扼杀掉这种行为!

正确姿势

首先, 我们得明白的是, 一般的重启程序, 重启机器, 实际上是发送了 fin标识去对端来触发四次挥手发生, 所以对待孽缘, 还是得遵循规律, 从内部攻破..

方法一

在刚才的实验中, iptabls无法阻扰, 但仅仅是因为姿势不对而已, 换个姿势分分钟一刀两段:

[root@6913388a8a1e /]# iptables -A INPUT -p tcp --dport 5555 -j REJECT --reject-with tcp-reset
[root@6913388a8a1e /]# iptables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:5555 reject-with tcp-reset

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

加了这个, client一发消息就不再是苦苦等待了, 直接就被iptables打耳刮子了

[root@6913388a8a1e /]# nc localhost 5555 -p 6666
p
Ncat: Connection reset by peer.

ss的结果是:

[root@6913388a8a1e /]# ss -ant
State       Recv-Q Send-Q  Local Address:Port                 Peer Address:Port
ESTAB       0      0           127.0.0.1:5555                    127.0.0.1:6666

tcpdump更是见证了这电光火石的瞬间, 第二个报文的R, 就是 reset的 flags, 这样会client那边的链接直接重置断开.

09:59:55.472340 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 3009865367:3009865369, ack 1955226254, win 342, options [nop,nop,TS val 3290439 ecr 3289331], length 2
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0036 d667 4000 4006 6658 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 b366 e697 748a 628e 8018
    0x0030:  0156 fe2a 0000 0101 080a 0032 3547 0032
    0x0040:  30f3 700a
09:59:55.472362 IP 127.0.0.1.5555 > 127.0.0.1.6666: Flags [R], seq 1955226254, win 0, length 0
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0028 0000 4000 4006 3cce 7f00 0001 7f00
    0x0020:  0001 15b3 1a0a 748a 628e 0000 0000 5004
    0x0030:  0000 fe1c 0000

但是 TCP的全双工的呀, 刚才断的只是, clientserver的, 还有serverclient的,
按照刚才的方法, 反过来搞一发!

PS: 要注意先把刚才的 INPUT 的删掉, 要不然reset就会被自己禁掉, 无法发送给 5555了..

root@6913388a8a1e /]# iptables -F
[root@6913388a8a1e /]# iptables -A OUTPUT -p tcp --dport 6666 -j REJECT --reject-with tcp-reset
[root@6913388a8a1e /]# iptables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:6666 reject-with tcp-reset

等到server一发送消息, 也马上挂了

[root@6913388a8a1e /]# nc -l -p  5555
p
[root@6913388a8a1e /]#

ss的结果:

[root@6913388a8a1e /]# ss -nat
State       Recv-Q Send-Q  Local Address:Port                 Peer Address:Port

而一边吃瓜看戏的tcpdump..:

14:54:59.045584 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [R], seq 379940499, win 0, length 0
    0x0000:  0000 0000 0000 0000 0000 0000 0800 4500
    0x0010:  0028 0000 4000 4006 3cce 7f00 0001 7f00
    0x0020:  0001 1a0a 15b3 16a5 6e93 0000 0000 5004
    0x0030:  0000 fe1c 0000


# 这里可能会有点疑问, 为什么从server 5555 发出的报文会没看到, 我猜测是因为这个报文还没到tcpdump就已经被iptables处理并直接返回了..

于是这对冤家就这样各奔东西, 相忘于江湖.

方法二

虽然这个方法比较好使, 但是操作起来真的挺恶心..而且还挺容易误伤, 所以有第二种方法, 简单又优雅: tcpkill
直接:

tcpkill -1 -i eth0 port 5555

等到它们互相发送消息, 就能直接干掉了..
请在这里输入图片描述

tcpkill的原理和刚才的iptables相似, 也是发送了一个链接重置的R标志报文, 迫使对方关闭断开连接, 只是相对而言会比较智能一点, 因为它会自动构造报文并发送.
详情可以看: https://github.com/stanzgy/wi...

总结

其实到这里, 大家应该有些印象, 不管是第一种方法, 还是第二种方法, 都离不开那个神奇的R, 但这些又是什么?

这些就是在TCP通信过程中, 起着决定性的作用标志位flags, 主要有下面几个:

  • SYN: 表示建立连接,
  • FIN: 表示关闭连接,
  • ACK: 表示响应,
  • PSH: 表示有 DATA数据传输,
  • RST: 表示连接重置。

上面的方法所用到就是最后一种标志:RST重置链接

所以总得而言, iptablesDROP行为, 能够阻止链接的建立, 但是对于已经建立起来的链接, 顶多只能阻止数据的传输, 但是不能断开链接, 链接的断开应该只有下面几种可能:

  1. socket 的主动close, 也就是发送 fin报文 ( 应用层程序或者内核 )
  2. TCP链接的超时自动断开 ( 这个过程可能会比较耗时 )
  3. 伪造报文发送RST

除了上面的条件, 还有一个点需要注意的, 那就是:

在某些情况下, 哪怕对方关闭了, 但是自己也是无法感知的, 还是需要send一次, 通信一次, 触发了socket的错误, 例如 Connection reset by peer. 或者 Broken pipe等等, 才能知道自己可以关闭, 否则大家都不通信, 这样有心无力, 只能听天由命了!

欢迎各位大神指点交流, QQ讨论群: 258498217, 转载请注明来源: https://segmentfault.com/a/11...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LINUX: 在不重启各自socket程序情况下, 断开ESTAB的TCP链接 的相关文章

  • packages are looking for funding

    npm fund 此命令检索 如何为项目的依赖项提供资金支持的信息 如果没有提供软件包名称 将用树状结构中列出所有正在寻找资金支持的依赖项 其中有资金的类型和要访问的URL 如果提供了软件包名称 可以将尝试使用 browser config
  • 在win10系统安装mysql 8.0.21

    首先第一步进入 mysql下载 然后选择操作系统为 windows 其他下载 选择第一个下载 如图 等待下在完成后 解压到自己想要安装的文件夹 然后打开 mysql 8 0 21 winx64 文件夹 在此文件夹下建立配置文件 my ini
  • 基于红外人脸识别的汽车防盗系统

    基于红外人脸识别的汽车防盗系统总结 人脸识别技术的发展可谓是比较成熟了 目前 大部分的技术都是关于可见光人脸识别 该项技术容易受到光照强度和角度的影响 发生假冒现象 近红外人脸识别克服了可见光的影响 将其与汽车防盗进行结合 解决了大量的传统
  • Springboot整合activiti(最详细版)

    写在最前 flowable和activiti本是一家 所以有很多api和设计是一样的 这里用的api是flowable的 流程设计器war包也可以共用 建议搜不到activiti某些资料的搜flowable的试试 1 导包
  • Capability CAP_DAC_OVERRIDE

    linux CAP DAC OVERRIDE and other permisions Unix Linux Stack Exchange
  • 复制列表—赋值、深浅拷贝与切片拷贝

    最近看了本书 你也能看得懂的Python算法书 觉得不错 分享下学习心得 这里是第一章 Python中列表存储的方法和其他语言中的不太一样 列表中的元素在计算机的存储空间中占据一定的内存 而列表本身存储的是这些元素的存储地址 在调用列表元素

随机推荐