13年时曾经遇到过一个问题(原谅我现在才写这篇文章…):提供下载服务的生产环境上(SUSE Linux,使用Tomcat BIO的Connector提供服务),有大量的状态为ESTABLISHED的连接存在。本来作为下载服务器,有大量连接存在是很正常的事情,但是由于数量远高于平时,引起维护人员的关注并只会到研发这边,我们通过观察这些链接的客户端IP和端口,发现大部分连接一直没有断开的迹象,tomcat创建的大量线程处于阻塞状态(BIO),服务器打开连接句柄过多,都可能会影响新的下载服务请求。情况很像是遇到了DDoS,当然也不排除是下载客户端存在BUG导致。 通过抓包发现,这些链接无一例外都处于一个无限循环中:每隔一段时间,服务端发送长度为0的KEEP-ALIVE,客户端回复ZeroWindow,如此循环。
滑动窗口的概念大家可以自行搜索,这里就不赘述了,大致的概念是发送和接受双方都各有一个发送窗口和一个接受窗口,其主要目的就是为了流量控制,使双方的发送、接收速度尽可能匹配。
当发送方的发送速度大于接收方的处理速度,接收方的缓冲塞满后,就会告诉发送方当前窗口size=0,请停止发送,发送方此时停止发送数据。
零窗口出现后,如何继续数据的传输呢。我们假设:接收方窗口更新为非0后,发送ACK给发送方,更新window的size,是一种可行的方式。但是如果这个ACK丢掉了(TCP不会给ACK回复ACK),那么发送方和接收方就会处于一种尴尬的局面,一个等着发,一个等着收,直到超时。 此时引入了坚持定时器的概念,由发送方主动创建,持续不断地询问接收方窗口是否更新,这个询问被称为窗口探测(window probes)。 终于到了问题的关键,这个定时器没有时间限制,意味着这个TCP连接会永远保持下去。
google了很久,没有查到各大操作系统对于坚持定时器的时间设置,提到零窗口攻击的信息也很少,截取其中一个明确提到零窗口攻击的页面,来自一个硬件负载均衡的厂商的产品介绍,貌似硬件防火墙或者负载均衡设备应该有相关的防护设置: 原链接
这个现象还是很容易重现的,当时使用IE浏览器(6还是7来着),直接下载一个文件,在弹出确认提示框后不要点击保存或者另存为,放着不动,用wireshark等工具查看,就会发现此时客户端服务端就进入了零窗口探测的状态,且会一直持续下去。 13年时chrome、firefox浏览器都不能重现,因为他们比较聪明,会自动开始下载,不给你确认的机会…现在19年的IE11也不重现了,确认框还是弹的,但是在我们不确认的情况下,后台已经下载完毕了。 这也难不倒我们,用Socket写个客户端,模拟建链后不接受数据的情况,服务端就用tomcat,随便在webapps下建个目录放一个zip文件用来下载。代码如下( 完整代码点击这里 ):
public class Client public static void main(String[] args) throws Exception Socket client = new Socket("10.253.178.218", 8080); client.setSoTimeout(10000); StringBuilder sb = new StringBuilder(); // HTTP协议中的换行符为CRLF,即\r\n,每一行请求消息头都需要以其结尾 sb.append("GET /fst/aaa.zip HTTP/1.1\r\n"); sb.append("Host: 10.253.178.218\r\n"); sb.append("Connection: keep-alive\r\n"); sb.append("Accept: */*\r\n"); sb.append("User-Agent: JavaSocket\r\n"); // 消息头结束需要单独一行CRLF sb.append("\r\n"); OutputStream os = client.getOutputStream(); InputStream is = client.getInputStream(); os.write(sb.toString().getBytes("UTF-8")); os.flush(); ...... // HTTP协议中的换行符为CRLF,即\r\n,读取到单独的一行\r\n意味着消息头读取完毕。 while (!"\r\n".equals(line)) line = readLine(is); // 模拟缓冲满的情况 Thread.sleep(1000000000000000000L); if (line.startsWith("Content-Length")) contentLength = Long.parseLong(line.split(":")[1].trim()); System.out.print(line); ...... 当时的办法 修改Tomcat源码,将socket对象暴露给业务代码,业务代码将所有socket和已经发送的字节数缓存起来(每次发送数据后即更新对应的字节数),每隔一段时间扫描一遍,将超过阈值时间后发送字节仍然没有变化的socket,强制调用socket.close()将其关闭。 socket.setSoLinger(true, 0); // socket是阻塞IO,数据不发送完默认是关不掉的,需要设置soLinger socket.close(); 写这篇文章时,试了一下NIO的Connector会如何,测试结果也不尽人意,坚持定时器仍然生效,过一会儿连接状态迁移为FIN_WAIT1(抓包里没看到发FIN,奇怪),连接句柄仍然被占用。 恐怖的是,./shutdown.sh关了tomcat,句柄仍不能释放,坚持定时器仍然在持续不断地探测…客户端已经利用TCP的机制,绑架了服务端操作系统层面的句柄资源? 作者对网络和系统层面了解不多,欢迎大家提出更优雅的解决方法,或者提出指正。 参考[1]: http://www.pcvr.nl/tcpip/tcp_pers.htm DHCP服务器-零配置接入网络 DHCP是动态主机配置协议(Dynamic Host Configuration Protocol)的简写,使用DHCP协议可以为客户端主机自动分配TCP/IP信息,如IP地址、子网掩码、网关、DNS等信息,服务器可以选择固定分配特定参数信息给指定的一台主机,也可以设置多台主机分享这些参数信息,所有客户端竞争 获得TCP/IP参数信息。客户端主机通过UDP广播的形式发送请求给本地网络,服务器收到请求后根据配置将TCP/IP信息租赁给客户端,时间是有限的,当租期到了以后客户端可以再次向服务器发出请求实现续约。在本书第一章大规模部署操作系统的实施过程中就需要使用DHCP服务为大量主机自动动态分配网络参数信息。在企业办公环境中使用DHCP服务可以帮助员工实现移动办公,不管是台式机、笔记本还是平板电脑只要接入网络就可以自动获得网络参数。 android屏幕到pc 安卓转显示器 显示器带USB接口,这事儿已经不稀奇了,有些小伙伴买的新型中高端显示器甚至还有USB Type-C接口呢。不知道大家会咋用这些接口,你说啥?和其他USB一样呗,接个U盘、键盘啥的挺方便……那小编只能说“太浪费啦!!!”,其实既然搭了Type-C接口,这几个功能是一定要试一下的,绝对让你感觉物有所值。 Type-C接口能支持的标准超级多,小编也不确定大家的显示器支持到“哪一层”,所 python eml邮件转为pdf python email模块 ____tz_zsSMTPemail 模块:负则构造邮件 smtplib 模块:负则发送邮件一、发送纯文本邮件from email.header import Header from email.mime.text import MIMEText import re import smtplib # 发送者、接收者 from_addr = 'xxxname@yyy.cn' password =
修改Tomcat源码,将socket对象暴露给业务代码,业务代码将所有socket和已经发送的字节数缓存起来(每次发送数据后即更新对应的字节数),每隔一段时间扫描一遍,将超过阈值时间后发送字节仍然没有变化的socket,强制调用socket.close()将其关闭。
socket.setSoLinger(true, 0); // socket是阻塞IO,数据不发送完默认是关不掉的,需要设置soLinger socket.close(); 写这篇文章时,试了一下NIO的Connector会如何,测试结果也不尽人意,坚持定时器仍然生效,过一会儿连接状态迁移为FIN_WAIT1(抓包里没看到发FIN,奇怪),连接句柄仍然被占用。 恐怖的是,./shutdown.sh关了tomcat,句柄仍不能释放,坚持定时器仍然在持续不断地探测…客户端已经利用TCP的机制,绑架了服务端操作系统层面的句柄资源? 作者对网络和系统层面了解不多,欢迎大家提出更优雅的解决方法,或者提出指正。 参考[1]: http://www.pcvr.nl/tcpip/tcp_pers.htm DHCP服务器-零配置接入网络 DHCP是动态主机配置协议(Dynamic Host Configuration Protocol)的简写,使用DHCP协议可以为客户端主机自动分配TCP/IP信息,如IP地址、子网掩码、网关、DNS等信息,服务器可以选择固定分配特定参数信息给指定的一台主机,也可以设置多台主机分享这些参数信息,所有客户端竞争 获得TCP/IP参数信息。客户端主机通过UDP广播的形式发送请求给本地网络,服务器收到请求后根据配置将TCP/IP信息租赁给客户端,时间是有限的,当租期到了以后客户端可以再次向服务器发出请求实现续约。在本书第一章大规模部署操作系统的实施过程中就需要使用DHCP服务为大量主机自动动态分配网络参数信息。在企业办公环境中使用DHCP服务可以帮助员工实现移动办公,不管是台式机、笔记本还是平板电脑只要接入网络就可以自动获得网络参数。 android屏幕到pc 安卓转显示器 显示器带USB接口,这事儿已经不稀奇了,有些小伙伴买的新型中高端显示器甚至还有USB Type-C接口呢。不知道大家会咋用这些接口,你说啥?和其他USB一样呗,接个U盘、键盘啥的挺方便……那小编只能说“太浪费啦!!!”,其实既然搭了Type-C接口,这几个功能是一定要试一下的,绝对让你感觉物有所值。 Type-C接口能支持的标准超级多,小编也不确定大家的显示器支持到“哪一层”,所 python eml邮件转为pdf python email模块 ____tz_zsSMTPemail 模块:负则构造邮件 smtplib 模块:负则发送邮件一、发送纯文本邮件from email.header import Header from email.mime.text import MIMEText import re import smtplib # 发送者、接收者 from_addr = 'xxxname@yyy.cn' password =
socket.setSoLinger(true, 0); // socket是阻塞IO,数据不发送完默认是关不掉的,需要设置soLinger socket.close(); 写这篇文章时,试了一下NIO的Connector会如何,测试结果也不尽人意,坚持定时器仍然生效,过一会儿连接状态迁移为FIN_WAIT1(抓包里没看到发FIN,奇怪),连接句柄仍然被占用。 恐怖的是,./shutdown.sh关了tomcat,句柄仍不能释放,坚持定时器仍然在持续不断地探测…客户端已经利用TCP的机制,绑架了服务端操作系统层面的句柄资源? 作者对网络和系统层面了解不多,欢迎大家提出更优雅的解决方法,或者提出指正。
写这篇文章时,试了一下NIO的Connector会如何,测试结果也不尽人意,坚持定时器仍然生效,过一会儿连接状态迁移为FIN_WAIT1(抓包里没看到发FIN,奇怪),连接句柄仍然被占用。
恐怖的是,./shutdown.sh关了tomcat,句柄仍不能释放,坚持定时器仍然在持续不断地探测…客户端已经利用TCP的机制,绑架了服务端操作系统层面的句柄资源?
作者对网络和系统层面了解不多,欢迎大家提出更优雅的解决方法,或者提出指正。
参考[1]: http://www.pcvr.nl/tcpip/tcp_pers.htm DHCP服务器-零配置接入网络 DHCP是动态主机配置协议(Dynamic Host Configuration Protocol)的简写,使用DHCP协议可以为客户端主机自动分配TCP/IP信息,如IP地址、子网掩码、网关、DNS等信息,服务器可以选择固定分配特定参数信息给指定的一台主机,也可以设置多台主机分享这些参数信息,所有客户端竞争 获得TCP/IP参数信息。客户端主机通过UDP广播的形式发送请求给本地网络,服务器收到请求后根据配置将TCP/IP信息租赁给客户端,时间是有限的,当租期到了以后客户端可以再次向服务器发出请求实现续约。在本书第一章大规模部署操作系统的实施过程中就需要使用DHCP服务为大量主机自动动态分配网络参数信息。在企业办公环境中使用DHCP服务可以帮助员工实现移动办公,不管是台式机、笔记本还是平板电脑只要接入网络就可以自动获得网络参数。 android屏幕到pc 安卓转显示器 显示器带USB接口,这事儿已经不稀奇了,有些小伙伴买的新型中高端显示器甚至还有USB Type-C接口呢。不知道大家会咋用这些接口,你说啥?和其他USB一样呗,接个U盘、键盘啥的挺方便……那小编只能说“太浪费啦!!!”,其实既然搭了Type-C接口,这几个功能是一定要试一下的,绝对让你感觉物有所值。 Type-C接口能支持的标准超级多,小编也不确定大家的显示器支持到“哪一层”,所 python eml邮件转为pdf python email模块 ____tz_zsSMTPemail 模块:负则构造邮件 smtplib 模块:负则发送邮件一、发送纯文本邮件from email.header import Header from email.mime.text import MIMEText import re import smtplib # 发送者、接收者 from_addr = 'xxxname@yyy.cn' password =
参考[1]: http://www.pcvr.nl/tcpip/tcp_pers.htm
DHCP是动态主机配置协议(Dynamic Host Configuration Protocol)的简写,使用DHCP协议可以为客户端主机自动分配TCP/IP信息,如IP地址、子网掩码、网关、DNS等信息,服务器可以选择固定分配特定参数信息给指定的一台主机,也可以设置多台主机分享这些参数信息,所有客户端竞争 获得TCP/IP参数信息。客户端主机通过UDP广播的形式发送请求给本地网络,服务器收到请求后根据配置将TCP/IP信息租赁给客户端,时间是有限的,当租期到了以后客户端可以再次向服务器发出请求实现续约。在本书第一章大规模部署操作系统的实施过程中就需要使用DHCP服务为大量主机自动动态分配网络参数信息。在企业办公环境中使用DHCP服务可以帮助员工实现移动办公,不管是台式机、笔记本还是平板电脑只要接入网络就可以自动获得网络参数。
显示器带USB接口,这事儿已经不稀奇了,有些小伙伴买的新型中高端显示器甚至还有USB Type-C接口呢。不知道大家会咋用这些接口,你说啥?和其他USB一样呗,接个U盘、键盘啥的挺方便……那小编只能说“太浪费啦!!!”,其实既然搭了Type-C接口,这几个功能是一定要试一下的,绝对让你感觉物有所值。 Type-C接口能支持的标准超级多,小编也不确定大家的显示器支持到“哪一层”,所
____tz_zsSMTPemail 模块:负则构造邮件 smtplib 模块:负则发送邮件一、发送纯文本邮件from email.header import Header from email.mime.text import MIMEText import re import smtplib # 发送者、接收者 from_addr = 'xxxname@yyy.cn' password =