摘要:一說起就是什么三次握手四次揮手而這次想討論的是在不重啟各自程序情況下將鏈接斷開情景模擬簡單點(diǎn)在同一個(gè)機(jī)器通過來實(shí)現(xiàn)和吧上面的意思就是端在端口監(jiān)聽而通過端口去連接為了更加清晰的看到流量咱們通過來觀察因?yàn)槭潜緳C(jī)所以才能捕獲而的結(jié)果也
一說起TCP, 就是什么三次握手, 四次揮手. 而這次想討論的是:
在不重啟各自socket程序情況下, 將ESTABLED鏈接斷開 ???情景模擬
簡單點(diǎn), 在同一個(gè)機(jī)器 通過 nc 來實(shí)現(xiàn) server 和 client 吧
# Server nc -l -p 5555
# Client nc localhost 5555 -p 6666
上面的意思就是, server端在5555端口監(jiān)聽, 而client 通過 6666 端口去連接
為了更加清晰的看到流量, 咱們通過 tcpdump 來觀察:
tcpdump -i lo -xnn -S # 因?yàn)槭潜緳C(jī), 所以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的結(jié)果也證明了鏈接已經(jīng)建立了:
[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
鏈接建立之后, 就能互相通信了
那么如何斷開這個(gè)鏈接呢?
錯(cuò)誤姿勢現(xiàn)在來試下傳統(tǒng)方法, 一般我們會(huì)上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
上面的規(guī)則, 意思就是將 目的端口為 5555 的請(qǐng)求丟棄了, 所以我們必須從客戶端發(fā)信息, 因?yàn)榭蛻舳瞬拍馨l(fā)到5555端口:
這里已經(jīng)看到, 信息已經(jīng)發(fā)不出去了, 而通過tcpdump能看到一堆從client發(fā)送的報(bào)文:
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真的已經(jīng)在努力了, 但是server卻不響應(yīng), 這真不怪server絕情, 而是它真的沒有收到! 都被那可惡的iptables丟掉了.!
那client會(huì)因?yàn)?b>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也不會(huì)輕易放棄.
而且很有意思的是, tcpdump還在持續(xù)的輸出:
.....(省略賊多的信息) 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
比較細(xì)心的同學(xué), 可能就會(huì)發(fā)現(xiàn), 它們通信的時(shí)間, 在不斷的增加, 從一開始幾毫秒, 到現(xiàn)在的2分鐘, 這是由TCP協(xié)議中的RTT 和RTO所決定的.
RTT (round trip time)
在開啟了TCP時(shí)間戳后,A記錄下時(shí)間t1把包發(fā)給B,B收到包后記錄下時(shí)間t2把包回給A ,這個(gè)過程里t2-t1就是RTTRTO(Retransmission TimeOut)即重傳超時(shí)時(shí)間
所以為了節(jié)省性能, client重試的時(shí)間, 會(huì)隨著這套算法, 不斷的增加~ 但是他們的鏈接, 已經(jīng)會(huì)長存...至于長存多久, 這個(gè)真的取決很多因素, 例如keepalived?;顧C(jī)制等等, 在這里, nc大概13分鐘就看不下去了...
那么假設(shè), 還沒到那么長時(shí)間 而且 iptables良心發(fā)現(xiàn)了, 放棄了阻擾, 它們又會(huì)怎樣呢?
[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結(jié)束時(shí), server就能接收到相應(yīng)的信息了, 從此client 和 Server又能愉快的玩耍了
花了很大的篇幅來證明client 和 server的真愛, 事實(shí)證明它們的專一值得學(xué)習(xí)!
但是很多時(shí)候, 如果client和server冷戰(zhàn), 誰也不理誰, 這就讓我們很蛋疼了, 因?yàn)槿绻@樣不必要的鏈接, 長時(shí)間保存, 會(huì)大量的占用資源, 很快就會(huì)出現(xiàn)資源瓶頸, 所以我們一定要扼殺掉這種行為!
正確姿勢首先, 我們得明白的是, 一般的重啟程序, 重啟機(jī)器, 實(shí)際上是發(fā)送了 fin標(biāo)識(shí)去對(duì)端來觸發(fā)四次揮手發(fā)生, 所以對(duì)待孽緣, 還是得遵循規(guī)律, 從內(nèi)部攻破..
方法一在剛才的實(shí)驗(yàn)中, iptabls無法阻擾, 但僅僅是因?yàn)樽藙莶粚?duì)而已, 換個(gè)姿勢分分鐘一刀兩段:
[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
加了這個(gè), client一發(fā)消息就不再是苦苦等待了, 直接就被iptables打耳刮子了
[root@6913388a8a1e /]# nc localhost 5555 -p 6666 p Ncat: Connection reset by peer.
而ss的結(jié)果是:
[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更是見證了這電光火石的瞬間, 第二個(gè)報(bào)文的R, 就是 reset的 flags, 這樣會(huì)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的,
按照剛才的方法, 反過來搞一發(fā)!
PS: 要注意先把剛才的 INPUT 的刪掉, 要不然reset就會(huì)被自己禁掉, 無法發(fā)送給 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一發(fā)送消息, 也馬上掛了
[root@6913388a8a1e /]# nc -l -p 5555 p [root@6913388a8a1e /]#
ss的結(jié)果:
[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 # 這里可能會(huì)有點(diǎn)疑問, 為什么從server 5555 發(fā)出的報(bào)文會(huì)沒看到, 我猜測是因?yàn)檫@個(gè)報(bào)文還沒到tcpdump就已經(jīng)被iptables處理并直接返回了..
于是這對(duì)冤家就這樣各奔東西, 相忘于江湖.
方法二雖然這個(gè)方法比較好使, 但是操作起來真的挺惡心..而且還挺容易誤傷, 所以有第二種方法, 簡單又優(yōu)雅: tcpkill
直接:
tcpkill -1 -i eth0 port 5555
等到它們互相發(fā)送消息, 就能直接干掉了..
tcpkill的原理和剛才的iptables相似, 也是發(fā)送了一個(gè)鏈接重置的R標(biāo)志報(bào)文, 迫使對(duì)方關(guān)閉斷開連接, 只是相對(duì)而言會(huì)比較智能一點(diǎn), 因?yàn)樗鼤?huì)自動(dòng)構(gòu)造報(bào)文并發(fā)送.
詳情可以看: https://github.com/stanzgy/wi...
其實(shí)到這里, 大家應(yīng)該有些印象, 不管是第一種方法, 還是第二種方法, 都離不開那個(gè)神奇的R, 但這些又是什么?
這些就是在TCP通信過程中, 起著決定性的作用標(biāo)志位flags, 主要有下面幾個(gè):
SYN: 表示建立連接,
FIN: 表示關(guān)閉連接,
ACK: 表示響應(yīng),
PSH: 表示有 DATA數(shù)據(jù)傳輸,
RST: 表示連接重置。
上面的方法所用到就是最后一種標(biāo)志:RST重置鏈接
所以總得而言, iptables的DROP行為, 能夠阻止鏈接的建立, 但是對(duì)于已經(jīng)建立起來的鏈接, 頂多只能阻止數(shù)據(jù)的傳輸, 但是不能斷開鏈接, 鏈接的斷開應(yīng)該只有下面幾種可能:
socket 的主動(dòng)close, 也就是發(fā)送 fin報(bào)文 ( 應(yīng)用層程序或者內(nèi)核 )
TCP鏈接的超時(shí)自動(dòng)斷開 ( 這個(gè)過程可能會(huì)比較耗時(shí) )
偽造報(bào)文發(fā)送RST
除了上面的條件, 還有一個(gè)點(diǎn)需要注意的, 那就是:
在某些情況下, 哪怕對(duì)方關(guān)閉了, 但是自己也是無法感知的, 還是需要send一次, 通信一次, 觸發(fā)了socket的錯(cuò)誤, 例如 Connection reset by peer. 或者 Broken pipe等等, 才能知道自己可以關(guān)閉, 否則大家都不通信, 這樣有心無力, 只能聽天由命了!
歡迎各位大神指點(diǎn)交流, QQ討論群: 258498217, 轉(zhuǎn)載請(qǐng)注明來源: https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/41421.html
閱讀 2996·2021-10-12 10:17
閱讀 1599·2021-09-01 11:38
閱讀 1094·2019-08-30 15:44
閱讀 3490·2019-08-26 18:36
閱讀 521·2019-08-26 13:25
閱讀 1891·2019-08-26 10:29
閱讀 2845·2019-08-23 15:58
閱讀 768·2019-08-23 12:59