- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
我们的程序,在实际的网络部署时,一般比较复杂,会经过很多的网络设备,防火墙就是其中的一种。做开发的同事,一般对这块了解不多,也很可能被防火墙坑到。比如,应用一般需要访问数据库,为了避免频繁建立连接,一般是会提前建立一个连接池,每次来一个请求,就从连接池取一个连接来用,用完再归还到池子里.
连接池中的连接是啥呢,其实就是和数据库之间的完成了三次握手后的socket,这个socket在白天时,一般经常有数据传输,而到了凌晨这种,可能就很少数据传输,等到了第二天,某个请求来了,从池子里取了某个socket,就直接发送数据,此时,很可能就会出现一个read timeout的情况.
为啥呢,是数据库不返回数据吗?不一定,如果应用服务器和db服务器之间,经过了防火墙的话,很可能,你这个socket发出去的包,直接就防火墙给丢弃了,根本没有到达数据库。如果想搜索这块相关的案例,可以搜索“防火墙 长连接”关键字.
现在的防火墙,基本都是有状态的,什么叫做有状态呢?以tcp为例,当服务端收到第一次握手(syn)时,此时,会认为这是要新建立连接,当三次握手完成后,再收到客户端在该socket上发来的请求报文时,此时,就知道这个报文不再是要新建立连接了,socket的状态此时也是established。说白了,握手时候的报文和握手完成后的报文,是不一样的,我们是可以区别对待的,这就是通俗意义上的“有状态”.
而在此之前,都是无状态防火墙(stateless firewall),不管是什么报文,都是一视同仁,没法根据状态来区分处理.
接下来,我们可以以iptables为例,理解下状态防火墙(iptables就是典型的状态防火墙).
该部分参考官网: https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html#STATEMACHINE 。
iptables是用户态中的命令,在内核中由netfilter实现,netfilter中实现连接的状态跟踪的机制,叫做conntrack(connection track)。在这个机制中,一共定义了4种状态:NEW, ESTABLISHED, RELATED,INVALID,UNTRACKED.
注意这里的conntrack,中文就是连接追踪,既然是连接,那么,我们怎么区分一个连接呢,比如,在tcp中,就是依靠四元组,这个四元组就可以唯一标识一个连接.
我们可以定义一个hash表,在收到一个tcp报文时,检查下这个四元组在hash表中是否存在(key就是四元组),如果不存在,我们就可以认为这个报文此时是新来的,也就是状态是new;当我们收到服务端返回的报文时,我们又可以检测下四元组在本地hash表是否存在,发现已经存在了,此时就可以认为状态是established.
这里大家如果要看这个状态的详细解释,可以查看官网文档;另外,我发现一篇文章也不错:
https://mp.weixin.qq.com/s/kv32lyWak4dCaPjMI4_6jw 。
NEW:
新建连接请求的数据包,且该数据包没有和任何已有连接相关联。 判断的依据是conntrack当前“只看到一个方向数据包(UNREPLIED)”,没有回包。
ESTABLISHED:
该连接是某NEW状态连接的回包,也就是完成了连接的双向关联。
其他几种状态, RELATED 和 INVALID 我感觉一般比较少用,至于 UNTRACKED ,可以等后续再慢慢领悟.
可以通过 cat /proc/net/nf_conntrack 查看连接状态.
如收到首次握手请求后,会看到如下内容:
tcp 6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.35 sport=1031 \
dport=23 [UNREPLIED] src=192.168.1.35 dst=192.168.1.5 sport=23 \
dport=1031 use=1
各个字段的意思:
tcp,协议名 。
6,传输层协议代号,其中tcp是6,udp是17.
117,ttl,表示这个conntrack的过期时间,类似于redis里的key的ttl 。
SYN_SENT,socket此时的状态。官方:The value SYN_SENT tells us that we are looking at a connection that has only seen a TCP SYN packet in one direction 。
src=192.168.1.5 dst=192.168.1.35 sport=1031 dport=23部分:
the source IP address, destination IP address, source port and destination port 。
UNREPLIED(未回复) 。
we have seen no return traffic for this connection,表示我们还没看到回包,现在只有单方向的包 。
src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 。
这是我们期待的回包的样子 。
tcp 6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.35 sport=1031 \
dport=23 src=192.168.1.35 dst=192.168.1.5 sport=23 dport=1031 \
use=1
收到syn+ack后,此时,我们的状态由SYN_SENT变成SYN_RECV,此时,也移除了之前的 UNREPLIED 关键字 。
tcp 6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.35 \
sport=1031 dport=23 src=192.168.1.35 dst=192.168.1.5 \
sport=23 dport=1031 [ASSURED] use=1
状态变成ESTABLISHED,增加了关键字:ASSURED 。
涉及到socket关闭的部分,导致的状态变化请参考官方文档: https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html#STATEMACHINE 。
简单来说,就是两边都完成关闭,状态才变成closed.
我们上面可以看到在一个connection上状态的变化,说白了,这部分就是给一个报文打上标记,关于状态的标记,后续我们才能基于这个状态label进行匹配.
所以,我们对于这个标记发生的时间,需要特别注意,可以看下,下图的左上角的绿色方块(connection tracking)就是状态标记发生的位置,基本上是最前面了(最上面是网卡,再下来是raw,再下来就是connection tracking了).
在这个绿色方块执行完成后,状态就已经标记好了,我们就可以根据状态来匹配这些报文来进行accept或者drop了,具体看下文.
我们的两台机器如下:
服务器:10.0.2.15:2222,监听2222端口 。
客户端:10.0.2.4 。
三次握手的报文都需要放行,第一次握手请求的特征是,此时,根据前面我们讲的状态,第一次握手到来时,本地是找不到该四元组的,所以state是new,再加上目的端口是2222,因此,我们的匹配条件是:
--dport 2222 -m state --state NEW ,因此,命令如下:
iptables -I INPUT 4 -p tcp --dport 2222 -m state --state NEW -j ACCEPT
此时,马上会收到2222端口返回的syn + ack,此时,状态就会变成established.
这种报文的特征是,state为established,这种怎么放心呢,这种其实默认就放心了,不需要我们再干啥.
第三次请求,也是同理,状态会被标记为established.
我们这边就需要模拟连接建立后,等conntrack记录过期后,客户端再使用这个socket会发生什么.
那么,我们怎么来查看这些conntrack呢,需要安装工具: yum install conntrack-tools 。
查看hash表
[root@node1 ~]# conntrack -L
tcp 6 431997 ESTABLISHED src=10.0.2.2 dst=10.0.2.15 sport=9526 dport=22 src=10.0.2.15 dst=10.0.2.2 sport=22 dport=9526 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
我们可以看到过期时间为431997,单位是秒,差不多是5天。这个初始值是432000,来自于内核参数:net.netfilter.nf_conntrack_tcp_timeout_established
[root@node1 ~]# sysctl -a |grep net.netfilter.nf_conntrack_tcp_timeout_established
net.netfilter.nf_conntrack_tcp_timeout_established = 432000
我们测试的话,这个时间就太长了,我们可以修改这个内核参数进行设置。我们可以改成60s过期:
[root@node1 ~]# vim /etc/sysctl.conf
net.netfilter.nf_conntrack_tcp_timeout_established = 60
执行sysctl -p
我们也可以选择手动删除、清空这些conntrack记录:
删除目标端口为2222的记录:
conntrack -D -p tcp --dport 2222
清空整个hash表
conntrack -F
实时监控hash表的变动(增删改)
conntrack -E
由于我们上一步执行了conntrack记录删除,此时,客户端发的报文,在iptables看来,又是全新的四元组,也就是会把状态标记为new,但是,这种请求报文和握手报文明显不同,这种报文的tcp层一般会设置psh标记,握手请求里则会设置syn等,所以,我们按照这个进行区分.
匹配条件为: --dport 2222 -m state --state NEW -m tcp --tcp-flags PSH PSH ,动作为丢弃:drop 。
iptables -I INPUT 4 -p tcp --dport 2222 -m state --state NEW -m tcp --tcp-flags PSH PSH -j DROP
ok,接下来开始测试.
用到了该博客中的客户端、服务端程序:
https://blog.csdn.net/fengcai_ke/article/details/125717134 。
server.c,主要是监听2222端口;client.c,就是连接并发送请求.
服务端发起监听,客户端发起连接,完成三次握手:
[root@node1 ~]# ./server
[root@node2 ~]# ./client
please input:
此时,可以看到三次握手:
服务端的 conntrack -E ,也可以看到hash表的变化:
[root@node1 ~]# conntrack -E
[NEW] tcp 6 120 SYN_SENT src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 [UNREPLIED] src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216
[UPDATE] tcp 6 60 SYN_RECV src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216
[UPDATE] tcp 6 432000 ESTABLISHED src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216 [ASSURED]
可以看到established的conntrack:
[root@node1 ~]# conntrack -L|grep 2222
conntrack v1.4.4 (conntrack-tools): 5 flow entries have been shown.
tcp 6 431857 ESTABLISHED src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
接下来,删除conntrack记录:
删除记录:
[root@node1 ~]# conntrack -D -p tcp --dport 2222
tcp 6 431800 ESTABLISHED src=10.0.2.4 dst=10.0.2.15 sport=46216 dport=2222 src=10.0.2.15 dst=10.0.2.4 sport=2222 dport=46216 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1
conntrack v1.4.4 (conntrack-tools): 1 flow entries have been deleted.
查询:
[root@node1 ~]# conntrack -L|grep 2222
conntrack v1.4.4 (conntrack-tools): 4 flow entries have been shown.
客户端使用该socket发起请求:
[root@node2 ~]# ./client
please input:11
send result
: Success
可以看到,服务端2222,没回复,客户端一直重传:
观察iptables,可以发现,确实匹配上了drop那条:
https://blog.csdn.net/fengcai_ke/article/details/125717134 。
https://blog.csdn.net/weixin_46768610/article/details/109354433 。
https://www.cnblogs.com/saolv/p/13096965.html 。
https://mp.weixin.qq.com/s/kv32lyWak4dCaPjMI4_6jw 。
https://mp.weixin.qq.com/s/bXWAd62R8-g0PyInB9MFIQ 。
https://mp.weixin.qq.com/s/vTys4GJH_tH5jGrDmR2etw 。
最后此篇关于数据库连接池长时间不用,乍一用还用不了,结果是防火墙的锅的文章就讲到这里了,如果你想了解更多关于数据库连接池长时间不用,乍一用还用不了,结果是防火墙的锅的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我接到了一项任务,要动态管理内存以超越 malloc 的速度。一些要求: 1) 有一个指向结构的指针 2)使用内存“ block ” 3) 内存将通过这样的调用分配 init(memory * mem
是否可以在不使用 visual studio 中的设计器的情况下创建 Crystal 报表文件?我的意思是在 C# 代码中以编程方式创建它。我没有找到任何有用的教程来做到这一点。 最佳答案 使用 Re
我注意到在没有必要的地方使用 Redux,例如我有一个加载该帖子的帖子页面,它是评论。 它们只在该页面上呈现,其他地方不需要,我可以将数据从我的 Post 容器传递给子 Prop 。 Now I wo
我有点困惑。如何在没有 goto 的情况下重构此语句? if(first_expression) { // .... if(second_expression) {
我正在构建一个 JScrollPane 来设置可滚动的 JPanel,其中包含一个 JTextPane 和一个 JTable。 当我加载框架时,我用很长的文本初始化 JTextPane,然后滚动条向下
如何不使用group concat将多行合并为一行? 考虑我有如下表格: 表用户: uid | name ----------- 1 | A 2 | B 表元: uid | metaid |
除了 NSUserDefaults 还有什么其他方法可以保存和取回自定义对象?对我来说,NSUserDefaults 变得太麻烦了(它没有正确保存),所以我正在寻找另一种保存数据的方法。 (我已经贴出
为简洁起见,请引用 my template 的描述(您的网络浏览器可能会给您关于该网站的误报),fiddle用我的一大块代码和我的 H.T.M.L 示例制作。文件。
如何在没有 javascript 代码的情况下仅使用 html 和 css 制作模态? 我有一个无法使用 javascript 的元素,但我需要模态。 W3.CSS W3.CSS Mo
我想创建一个干净的解决方案来处理客户端丢失的图像 使用 到目前为止,handleErrors 看起来像这样: function handleErrors() { image.onerror
有没有一种方法可以不使用 JavaScript 来更改指定元素的 CSS 样式? 我不想使用 JS,因为有些浏览器不支持,或者已经禁用它... 具体例子: 我有 2 个 DIV(#menu 和 #co
如何在不使用 LINQ 的情况下过滤数据表?我目前正在使用 .NET 2.0;因此,我无法使用 LINQ。我有一个返回房间/价格对的存储过程。我想过滤数据表,以便它会选择特定房间的所有费率,所以基本上
我正在使用 jQuery triggerHandler() 触发一些 DOM 事件 stackoverflow $(document).ready(function(
这个问题在这里已经有了答案: Cleanest way to write retry logic? (30 个答案) 关闭 9 年前。 有没有更好的方法可以不使用 goto 来编写这段代码?这看起来
我试图在没有 jquery 的情况下找到具有特定标签名称的最接近的元素。当我点击 我想访问 对于那张 table 。建议?我阅读了有关偏移量的信息,但并没有真正了解太多。我应该只使用: 假设 th 已
我想将从 ajax 调用获得的结果转换为 JavaScript 数组。如何在不使用 jQuery 的情况下做到这一点? 或者也可以只循环 json 数组而不转换为 JavaScript 数组。 现在我
我需要具有随机非重复值的数组。我发现用 includes() 解决问题,但我想在没有它的情况下进行。 代码 function rand(min, max){ return Math.round
我是网络编程的新手(即不到一个月)。 我想做的是为 FAQ 列表创建一个漂亮的平滑隐藏/显示效果,如本网站所示: http://www.hl.co.uk/pensions/sipp/frequentl
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求提供代码的问题必须表现出对所解决问题的最低限度理解。包括尝试过的解决方案、为什么它们不起作用,以及预
这个问题在这里已经有了答案: How to pass arguments to an included file? (9 个回答) 关闭 6 年前。 所以我的索引页是这样的 "") {
我是一名优秀的程序员,十分优秀!