模拟百万级TCP并发

开发 开发工具
TCP并发是指一个服务器同时“hold住”的连接数量,确切的说就是指服务器端看到的“ESTABLISHED”状态的TCP连接数量。

什么是TCP并发

TCP并发是指一个服务器同时“hold住”的连接数量,确切的说就是指服务器端看到的“ESTABLISHED”状态的TCP连接数量。通过netstat -n|grep ^tcp|awk '{print $NF}'|sort -nr|uniq -c可以查看当前服务器TCP状态统计报告,下图是我的执行结果(我正在通过SSH连接这台机器所以有一个“ESTABLISHED”状态的TCP连接)

测试TCP并发就是指让这个值达到的顶峰,要实现这个必须满足两点:

  • 短时间内构造百万级连接
  • 服务器端同时hold住百万级连接

需要注意的是上面的“测试”不包括“连接之后的交互”仅仅是指“hold住连接”。

传统工具为什么无法满足

很多服务器都是TCP结构的比如Mysql、Tomcat、Nginx,这些工具也有相应的压力测试工具,比较著名的包括:Jmeter、Tsung。这些工具的实现基本上是一致的

  • 同时启动多个任务
  • 每个任务打开一个socket连接到服务器

这种测试方法受限于三个资源

  • 可以启动的任务数量(线程数或者进程数)
  • 可以打开的socket数量(文件描述符)
  • 受限于本机可用端口最大值——65535

第一个限制我们可以通过“协程”之类的技术手段解决;第二个限制在内存满足的情况下可以通过调整系统参数解决(参考我的《你真知道“Too many open files”?》);第三个限制几乎是致命的——传统上只能通过多台服务器一块协同。

即便解决了上述三个问题也很难在“短时间”内造成巨大的压力,大量的socket会吃光内存,多台服务器协同必然是一个分布式问题(想想就掉头发)。

新的思路

TCP连接给人的感觉是一个“通道”,这其实这是一个“错觉”。所有的网络基本上都是基于“存储转发”的经过。三次握手之后的TCP连接到达“ESTABLISHED”状态,服务器会为它保留资源——即使客户端已经不再理睬这个连接。那么我们是不是可以不经过TCP/IP协议栈直接通过raw socket构造三次握手呢?只要我们大批量的构造三次握手就可以对服务器构成巨大的压力了。

我们重点关注Client->Server的两个箭头。第一个数据包是SYN数据包,seq=随机数;第二个数据包是ACK数据包,ACK=收到数据包的seq+1,seq=收到数据包的ack。其实TCP数据包之间是没有直接关系的,我们收到一个数据包就可以直接算出回复数据包的ack、seq

上面的思路基本上可以证明我们的方法在理论上是可行的,在实践上我们还需要克服一些问题

  • 怎么获取Server到Client的SYN+ACK(三次握手中的第二个箭头);毕竟我们不是直接使用Socket打开TCP连接(这样做就不需要自己构造TCP三次握手了)
  • Server在收到Client请求后会尝试对Client进行ARP地址,如果发现无法解析就认为是一个非法的数据包直接发送RST数据包关闭TCP连接

第一个问题我们通过libpcap“旁路”kernel,直接获取原始数据包。下图是libpcap的原理

libpcap底层使用的是BPF(Berkeley Packet Filter)驱动,这是kernel中专门用来“调试”的驱动程序最早是为Unix开发现在已经成为各种操作系统的标配(只要支持tcpdump那么底层一定是有实现这个驱动模型的)。它独立于kernel中的其他协议栈直接和读取数据链路层的数据包。

通过libpcap我们可以获取所有的数据包(即便操作系统不处理)然后构造自己的数据包通过raw socket直接把写入到数据链路层。整个“收包”->“处理”->“回包”完全不需要kernel参与。

第二个问题其实在前面的文章中我已经给出了答案——构造并且回复ARP数据包(《深入理解ARP攻击 》)。简单来说就是通过libpcap获取arp request,通过raw socket回复arp response。

动手

我努力去掉所有不相关的东西只保留了最精简的部分,不到300行的代码。代码分为两大部分“发起TCP SYN数据包”和“回复SYN+ACK数据包、ARP request数据包”。具体内容可以看这里

这次我特意放上了cmake文件,执行以下cmake就可以编译了。

https://github.com/fireflyc/million-tcp-client

模拟多个IP地址

受限于端口上限,一台服务器只能模拟65535个TCP连接。但是我提供的演示程序是可以指定IP地址的,这个IP地址只需要和目标IP在同一个网络内就可以了。比如我的测试环境:

测试机器有自己的IP地址172.16.46.128,但是这个IP地址并没有用途,只是为了方便我SSH连接。服务器的IP地址是172.16.46.133,我启动三个tcp-client分别绑定200、201、202。

每个TCP-Client进程都可以模拟65535个TCP连接。(这个其实还有改进的余地)

【本文是51CTO专栏作者邢森的原创文章,转载请联系作者本人获取授权】

 

责任编辑:武晓燕 来源: 写程序的康德
相关推荐

2009-03-12 09:44:05

高并发开源数据库MySQL

2011-11-03 13:59:05

2019-12-31 10:33:57

Netty高性能内存

2011-08-23 17:12:22

MySQL支撑百万级流

2014-02-10 16:27:09

百万级IOPSOceanStor 1

2020-01-13 10:20:30

架构聊天架构百万并发量

2019-02-12 09:34:00

微博短视频架构

2021-02-18 22:18:50

TCP 服务器源码

2024-12-04 13:52:30

2022-09-09 08:41:43

Netty服务端驱动

2021-05-24 10:55:05

Netty单机并发

2021-02-03 06:15:26

工具postManHttp

2012-02-01 16:32:32

2023-10-25 11:20:09

快手电商混合云容器云

2010-07-23 08:48:21

PHP架构

2020-02-06 08:03:53

疫情设计IM系统

2009-08-06 19:12:07

2019-02-27 09:46:05

数据库架构并发

2019-03-06 09:36:12

Kafka缓存磁盘

2019-09-23 08:46:04

零拷贝 CPU内存
点赞
收藏

51CTO技术栈公众号