一提及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...