LINUX: 在不重啓各自socket程序狀況下, 斷開ESTAB的TCP連接

一提及TCP, 就是什麼三次握手, 四次揮手. 而此次想討論的是:git

在不重啓各自socket程序狀況下, 將ESTABLED連接斷開 ???

情景模擬

簡單點, 在同一個機器 經過 nc 來實現 serverclientgithub

# 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協議中的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...

相關文章
相關標籤/搜索