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

一说起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就是RTTRTO(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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

浅谈拒绝服务攻击的原理与防御[1] | 普通拒绝服务攻击

普通拒绝服务攻击是指一些传统的攻击方式,如:SYN FLOOD攻击、ACK FLOOD攻击、CC攻击、UDP FLOOD攻击 等等,下面会详细介绍。 SYN...

3836
来自专栏非典型技术宅

Swift实践:使用CoreData完成上班签到小工具1. CoreData Stack的作用2.创建 CoreData Stack3. 一对多的关系4. 完成Demo,了解使用CoreData St

1223
来自专栏Jerry的SAP技术分享

利用CRM中间件Middleware从ERP下载Customer Material的常见错误

下图是我在ERP创建的Material,为其维护了一个Customer Material AOP。

3948
来自专栏c#开发者

HTML5手机APP开发入(5)

HTML5手机APP开发入(5) 回顾一下 HTML5手机APP开发入(4) 如何自定义Component,directive HTML5手机APP开发入(...

7146
来自专栏沈唁志

如何在CentOS 7上安装Asterisk

Asterisk是一个开源专用交换机(PBX)服务器,它使用会话发起协议(SIP)来路由和管理电话呼叫。值得注意的功能包括客户服务队列,待机音乐,电话会议和电话...

9493
来自专栏极客猴

一道关于 TCP 连接的题目

小陈点了点头表示很熟悉,然后一口气将 TCP 连接中三次握手和四次分手详细地说了一遍。心想暗笑,这问题难不倒我的,哈哈。

791
来自专栏JavaWeb

基于自定义注解和Aop动态数据源配置

5717
来自专栏IT技术精选文摘

TCP 的那些事儿

TCP是一个巨复杂的协议,因为他要解决很多问题,而这些问题又带出了很多子问题和阴暗面。所以学习TCP本身是个比较痛苦的过程,但对于学习的过程却能让人有很多收获。...

2599
来自专栏*坤的Blog

Sublime Text3注册码供研究使用

2145
来自专栏北京马哥教育

如何保证Linux服务器的安全

很少见有人马上为一台新安装的服务器做安全措施,然而我们生活所在的这个社会使得这件事情是必要的。不过为什么仍旧这么多人把它拖在最后?我已经做了相同的事情,它常常可...

3797

扫码关注云+社区

领取腾讯云代金券