当前位置: 首页 > Linux

LINUX:断开ESTAB的TCP连接,不重启各自的socket程序

时间:2023-04-06 20:39:13 Linux

说到TCP,就是三次握手,四次挥手。这次要讨论的是:断开ESTABLED链接而不重启各自的socket程序???简化场景模拟,在同一台机器上使用nc我们来实现server和client#servernc-l-p5555#Clientnclocalhost5555-p6666上面的意思是server端监听5555端口,client通过6666端口连接.为了更清楚的看到流量,我们使用tcpdump观察一下:tcpdump-ilo-xnn-S#因为是本地的,所以lo可以抓到08:32:01.063394IP127.0.0.1.6666>127.0.0.1.5555:Flags[S],seq1812097880,win43690,options[mss65495,sackOK,TSval2762998ecr2761788,nop,wscale7],length00x0000:000000000000000000000000080045000x0010:003c43ae40004006f90b7f0000017f000x0020:00011a0a15b36c026b5800000000a0020x0030:aaaafe3000000204ffd70402080a002a0x0040:28f6002a243c0103030708:32:01.063416IP127.0.0.1.5555>127.0.0.1.6666:Flags[S。400040063cba7f0000017f000x0020:000115b31a0a4eadba236c026b59a0120x0030:aaaafe3000000204ffd70402080a002a0x0040:28f6002a28f60103030708:32:01.063431IP127.0.0.1.6666>127.0.0.1.5555:Flags[.],ack1320008228,win342,options[nop,nop,TSval2762998ecr2762998],length00x0000:000000000000000000000000080045000x0010:003443af40004006f9127f0000017f000x0020:00011a0a15b36c026b594eadba2480100x0030:0156fe2800000101080a002a28f6002a0x0040:28f6并且ss的结果也证明链接已经建立:[root@5464f8622628/]#cPeSant-PortReendAddress-:PortESTAB00172.17.0.3:6666172.17.0.3:5555ESTAB00172.17.0.3:5555172.17.0.3:6666链接建立后,可以互相通信,那么如何断开这个链接呢?尝试传统的方法,通常我们会使用iptables:[root@6913388a8a1e/]#iptables-AINPUT-ptcp--dport5555-jDROP[root@6913388a8a1e/]#iptables-LChainINPUT(policyACCEPT)targetprotoptsourcedestinationDROPtcp--anywhereanywheretcpdpt:personal-agentChainFORWARD(policyACCEPT)targetprotoptsourcedestinationChainOUTPUT(policyACCEPT)targetprotoptsourcedestination上面的规则表示目的端口为5555的请求被丢弃,所以我们要从客户端发送信息,因为客户端只能发送到5555端口:可以看到这里信息还没有发送出去,通过tcpdump可以看到客户端发来的一堆信息Packet:08:43:44.459584IP127.0.0.1.6666>127.0.0.1.5555:Flags[P.],seq327893533:327893535,ack3568222208,win342,options[nop,nop,TS2832ecr36],length20x0000:000000000000000000000000080045000x0010:003675f840004006c6c77f0000017f000x0020:00011a0a15b3138b421dd4aec00080180x0030:0156fe2a00000101080a002b3bba002b0x0040:37ea330a08:43:44.670096IP127.0.0.1.6666>127.0.0.1.5555:Flags[P.],seq327893533:327893535,ack3568222208,win342,options[nop,nop,TSval2833359ecr2832362],length20x0000:000000000000000000000000080045000x0010:003675f940004006c6c67f0000017f000x0020:00011a0a15b3138b421dd4aec00080180x0030:0156fe2a00000101080A002B3BCF002B0x0040:37EA330A08:43:44.8881782IP127.0.0.0.1.6666>127.0.0.0.55555:flags[p.]length20x0000:000000000000000000000000080045000x0010:003675fa40004006c6c57f0000017f000x0020:00011a0a15b3138b421dd4aec00080180x0030:0156fe2a00000101080a002b3be4002b0x0040:37ea330a....(还剩8个左右)tcpdump的输出告诉我们,客户端真的很努力,但是服务器没有响应。这不是怪服务器绝情,而是真的没有收到!一切都被讨厌的iptables弄丢了!客户端会因为服务器没有响应而放弃连接吗?[root@6913388a8a1e/]#ss-antStateRecv-QSend-QLocalAddress:PortPeerAddress:PortESTAB02127.0.0.1:6666127.0.0.1:5555ESTAB00127.0.0.1:5555127.0.0.1:6666显然,他们之间是真爱,即使服务器没有响应,客户端也不会轻易放弃。而且有趣的是,tcpdump还在继续输出:.....(省略更多信息)08:53:28.844326IP127.0.0.1.6666>127.0.0.1.5555:Flags[P.],seq327893533:327893535,ack3568222208,Win342,选项[NOP,NOP,TSVal2891776ECR2832362],长度20x0000:0000000000000000000000000000000000080045000x0010:00367606760640004006C6B97F00000000001700017000000000000000000000000000000000000000000千i0002.00but000000000000000000000000000000000倍::0156FE2A00000101010A002C2000002B0x0040:37EA330A08:55:31.721921IP127.0.0.0.1.6666>127.0.0.0.0.0.0.1.5555:Flags[P.],NOP,TSVal2904064ECR2832362],长度20x0000:0000000000000000000008004500010:003676074006B87F00000x0020:0000a15b3118C0xAeAeD80d40000101080a002c5000002b0x0040:37ea330a比较细心的同学可能会发现他们的通信时间在不断增加,从开始的几毫秒到现在的2分钟,这是由TCP协议RTT中的RTT决定的(roundtriptime)由RTO决定启用TCP时间戳后,A记录时间t1并将数据包发送给B,B在收到数据包后记录时间t2并将数据包发送回A。在这个过程中,t2-t1是RTTRTO(RetransmissionTimeOut),也就是重传超时时间,所以为了节省性能,客户端重试时间会随着这个算法不断增加~但是他们的链接已经存在了...至于长期保存多久,这个真的取决于很多因素,比如keepalived机制等等,到这里,nc大概13分钟就受不了了。。。然后假设没那么久iptables良心发现已经放弃了blocking他们会发生什么?[root@6913388a8a1e/]#iptables-F[root@6913388a8a1e/]#iptables-nLChainINPUT(policyACCEPT)targetprotoptsourcedestinationChainFORWARD(policyACCEPT)targetprotoptsourcedestinationChainOUTPUT(policyACCEPT)targetprotoptsourcedestination在下一个RTO结束时,服务端就能收到相应的信息,从此客户端和服务端又可以愉快的玩耍了。证明客户端和服务器之间的真爱花了很多篇幅。原来他们的特殊性值得学习!但是很多时候,如果客户端和服务端处于冷战状态,谁都不关心对方,这让我们很苦恼,因为这种不必要的链接如果长期保存下来,会占用很多资源.很快就会出现资源瓶颈,一定要杀掉这种行为!正确姿势首先我们要明白,一般的重启程序,重启机器,其实是发送fin标志走到对面会触发四波,所以在对付煞气的时候,还是要循规蹈矩,从里面突破。方法一、刚才的实验,iptabls无法屏蔽,只是姿势不对而已。改变姿势每分钟两段:[root@6913388a8a1e/]#iptables-AINPUT-ptcp--dport5555-jREJECT--reject-withtcp-reset[root@6913388a8a1e/]#iptables-nLChainINPUT(policyACCEPT)targetprotoptsourcedestinationREJECTtcp--0.0.0.0/00.0.0.0/0tcpdpt:5555reject-withtcp-resetChainFORWARD(policyACCEPT)targetprotoptsourcedestinationChainOUTPUT(policyACCEPT)目标protoptsourcedestinationadded这个,client一发消息,就不再苦苦等待,直接被iptables打耳光[root@6913388a8a1e/]#nclocalhost5555-p6666pNcat:Connectionresetbypeer.而ss的结果是:[root@6913388a8a1e/]#ss-antStateRecv-QSend-QLocalAddress:PortPeerAddress:PortESTAB00127.0.0.1:5555127.0.0.1:6666tcpdump见证了这一刻的闪电和flint,第二条消息的R,是reset的flags,这样client端的链接会直接reset断开。09:59:55.472340IP127.0.0.1.1.1.1.0.0.0.5555:Flags[P.],SEQ3009865367:3009865369,ACK1955226254,Win342,选项[Not,NOP,TSVal3289331],LNGTH2。0x00:0000000000000000080045000x0010:0036d6674000400666587f0000017f000x0020:00011a0a15b3b366e697748a628e80180x0030:0156fe2a00000101080a0032354700320x0040:30f3700a09:59:55.472362IP127.0.0.1.5555>127.0.0.1.66666:Flags[R],SEQ1955226254,Win0,Length00x0000:0000000000000000000045000x0010:0028000040063cE7F0000x0010015B31A0A748E628E628E0x0030:0000fe1c0000ButTCPisfull-duplex,what刚才坏掉的只是客户端发给服务器,服务器发给客户端,按照刚才的方法,反向发送!删除INPUT,否则reset会被自己禁止,无法发送到5555..root@6913388a8a1e/]#iptables-F[root@6913388a8a1e/]#iptables-AOUTPUT-ptcp--dport6666-jREJECT--reject-withtcp-reset[root@6913388a8a1e/]#iptables-nLChainINPUT(policyACCEPT)targetprotoptsourcedestinationChainFORWARD(policyACCEPT)targetprotoptsourcedestinationChainOUTPUT(policyACCEPT)targetprotoptsourcedestinationREJECTtcp--0.0.0.0/00.0.0.0/06tcp6dpt:6reject-withtcp-reset等待服务器发送消息,立即挂断[root@6913388a8a1e/]#nc-l-p5555p[root@6913388a8a1e/]#ssresult:[root@6913388a8a1e/]#ss-natStateRecv-QSend-QLocalAddress:PortPeerAddress:Porttcpdump..:14:54:59.045584IP127.0.0.1.6666>127.0.0.1.5555:标志[R],seq379940499,Win0,长度00x0000:0000000000000000000000080045000x0010:0028000040063CE7F000000017F000X0020:00011A0A1A0A15B315B316A56E936E9300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000服务器5555发送的消息将不会被看到。我猜是因为消息在到达tcpdump之前,已经被iptables处理过,直接返回了。方法二这种方法虽然比较有效,但是操作起来确实有难度。恶心。。而且还挺容易误伤的,于是就有了第二种方法,简单优雅:直接tcpkill:tcpkill-1-ieth0port5555等到互相发消息,就可以直接kill掉了。。原理tcpkill的和前面的iptables类似,也是发送一个linkresetRflag报文,强制对方关闭并断开连接,但是相对来说更聪明一些,因为它会自动构造报文并发送。详情请看:https://github.com/stanzgy/wi...总结其实说到这里,大家应该有些印象了。不管是第一种方法还是第二种方法,都离不开神奇的R,但这些到底是什么呢?这些都是在TCP通信过程中,标志起着决定性的作用,主要有以下几种:SYN:表示建立连接,FIN:表示关闭连接,ACK:表示响应,PSH:表示有DATA数据传输,RST:表示连接复位。上面的方法使用最后一个标志:RST来重置链接所以一般情况下,iptables的DROP行为可以阻止链接的建立,但是对于已经建立的链接,最多只能阻止数据的传输,而不是断开链接,断开链接应该只有以下几种可能:主动关闭socket,即发送fin消息(应用层程序或内核),随着时间的推移自动断开TCP连接(这个过程可能会比较耗时)伪造除了上述消息发送RST的条件外,还有一点需要注意,那就是:在某些情况下,即使对方关闭了,但你自己是感知不到的,你仍然需要发送一次,通信一次,并触发socket错误,例如Connectionresetbypeer。或者Brokenpipe之类的,要知道可以关掉,不然大家沟通不起来,就很无奈了,只能认命了!欢迎指教交流,QQ讨论群:258498217,转载请注明出处:https://segmentfault.com/a/11...