一提及TCP
, 就是什么三次握手, 四次挥手. 而此次想讨论的是:git
在不重启各自socket程序状况下, 将ESTABLED连接断开 ???
简单点, 在同一个机器 经过 nc
来实现 server 和 client 吧github
# Server nc -l -p 5555
# Client nc localhost 5555 -p 6666
上面的意思就是, server端在5555端口监听, 而client 经过 6666 端口去链接算法
为了更加清晰的看到流量, 我们经过 tcpdump
来观察:segmentfault
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
连接创建以后, 就能互相通讯了socket
那么如何断开这个连接呢?tcp
如今来试下传统方法, 通常咱们会上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发送的报文:spa
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
协议中的RTT
和RTO
所决定的.
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
就能接收到相应的信息了, 今后client
和 Server
又能愉快的玩耍了
花了很大的篇幅来证实client
和 server
的真爱, 事实证实它们的专注值得学习!
可是不少时候, 若是client
和server
冷战, 谁也不理谁, 这就让咱们很蛋疼了, 由于若是这样没必要要的连接, 长时间保存, 会大量的占用资源, 很快就会出现资源瓶颈, 因此咱们必定要扼杀掉这种行为!
首先, 咱们得明白的是, 通常的重启程序, 重启机器, 其实是发送了 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
的全双工的呀, 刚才断的只是, client
到 server
的, 还有server
到client
的,
按照刚才的方法, 反过来搞一发!
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, 主要有下面几个:
上面的方法所用到就是最后一种标志:RST重置连接
因此总得而言, iptables
的DROP
行为, 可以阻止连接的创建, 可是对于已经创建起来的连接, 顶多只能阻止数据的传输, 可是不能断开连接, 连接的断开应该只有下面几种可能:
socket
的主动close
, 也就是发送 fin
报文 ( 应用层程序或者内核 )TCP
连接的超时自动断开 ( 这个过程可能会比较耗时 )RST
除了上面的条件, 还有一个点须要注意的, 那就是:
在某些状况下, 哪怕对方关闭了, 可是本身也是没法感知的, 仍是须要send
一次, 通讯一次, 触发了socket的错误, 例如 Connection reset by peer.
或者 Broken pipe
等等, 才能知道本身能够关闭, 不然你们都不通讯, 这样有心无力, 只能听天由命了!
欢迎各位大神指点交流, QQ讨论群: 258498217, 转载请注明来源: https://segmentfault.com/a/11...