摘要:一說起就是什么三次握手四次揮手而這次想討論的是在不重啟各自程序情況下將鏈接斷開情景模擬簡單點在同一個機器通過來實現和吧上面的意思就是端在端口監聽而通過端口去連接為了更加清晰的看到流量咱們通過來觀察因為是本機所以才能捕獲而的結果也
一說起TCP, 就是什么三次握手, 四次揮手. 而這次想討論的是:
在不重啟各自socket程序情況下, 將ESTABLED鏈接斷開 ???情景模擬
簡單點, 在同一個機器 通過 nc 來實現 server 和 client 吧
# 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協議中的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, 主要有下面幾個:
SYN: 表示建立連接,
FIN: 表示關閉連接,
ACK: 表示響應,
PSH: 表示有 DATA數據傳輸,
RST: 表示連接重置。
上面的方法所用到就是最后一種標志:RST重置鏈接
所以總得而言, iptables的DROP行為, 能夠阻止鏈接的建立, 但是對于已經建立起來的鏈接, 頂多只能阻止數據的傳輸, 但是不能斷開鏈接, 鏈接的斷開應該只有下面幾種可能:
socket 的主動close, 也就是發送 fin報文 ( 應用層程序或者內核 )
TCP鏈接的超時自動斷開 ( 這個過程可能會比較耗時 )
偽造報文發送RST
除了上面的條件, 還有一個點需要注意的, 那就是:
在某些情況下, 哪怕對方關閉了, 但是自己也是無法感知的, 還是需要send一次, 通信一次, 觸發了socket的錯誤, 例如 Connection reset by peer. 或者 Broken pipe等等, 才能知道自己可以關閉, 否則大家都不通信, 這樣有心無力, 只能聽天由命了!
歡迎各位大神指點交流, QQ討論群: 258498217, 轉載請注明來源: https://segmentfault.com/a/11...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41421.html
閱讀 2985·2021-10-12 10:17
閱讀 1589·2021-09-01 11:38
閱讀 1081·2019-08-30 15:44
閱讀 3479·2019-08-26 18:36
閱讀 507·2019-08-26 13:25
閱讀 1884·2019-08-26 10:29
閱讀 2835·2019-08-23 15:58
閱讀 759·2019-08-23 12:59