单机10万TCP连接测试记录
2023-10-10 15:00 古兆洋 阅读(154) 评论(0) 编辑 收藏 举报转自:https://www.cnblogs.com/fuhua/p/16904864.html
单机10万TCP连接测试记录
前言
现有两台Ubuntu服务器,一台名叫TcpServer,一台名叫TcpClient。
TcpServer用于监听Tcp连接,TcpClient用于发起Tcp连接。
现在想测试TcpServer是否能承受住10w+ TCP连接。
编程语言:C#
使用框架:TouchSocket
准备工作
安装DotNet6环境
服务端代码
using System.Text; using TouchSocket.Core.Config; using TouchSocket.Core.Dependency; using TouchSocket.Core.Log; using TouchSocket.Sockets; namespace TcpServer { internal class Program { //用于记录客户端数量 private static int _count = 0; //阻塞事件,防止客户端退出 private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { TcpService service = new TcpService(); service.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"有客户端连接:{((SocketClient)client).GetIPPort()} ---- 客户端数量:{_count}"); }; service.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"有客户断开:{((SocketClient)client).GetIPPort()} ---- 客户端数量:{_count}"); }; service.Setup( new TouchSocketConfig() .SetListenIPHosts(new IPHost[] {new IPHost(7790) }) .SetMaxCount(999999) //这里很重要,否则一分钟后服务端会删除无活动的连接 .SetClearInterval(-1)) .Start(); _closingEvent.WaitOne(); } } }
客户端代码
using System.Net.Sockets; using System.Text; using TouchSocket.Core.Config; using TouchSocket.Sockets; namespace TestTcpClient { internal class Program { //用于记录连接数量 private static int _count = 0; private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { for (int i = 0; i < 100000; i++) { try { TouchSocket.Sockets.TcpClient tcpClient = new TouchSocket.Sockets.TcpClient(); tcpClient.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; tcpClient.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; tcpClient.Setup("1.14.107.247:7790"); tcpClient.Connect(); } catch (Exception ex) { Console.WriteLine($"{i}:{ex.Message}"); if(ex.InnerException != null) { Console.WriteLine($"{i}:{ex.InnerException.Message}"); } } } _closingEvent.WaitOne(); } } }
编译
将TcpServer和TcpClient编译后分别放入TcpServer服务器和TcpClient服务器
测试记录
失败尝试1(Linux可用端口范围限制)
运行TcpServer:dotnet TcpServer.dll
运行TcpClient:dotnet TestTcpClient.dll
从服务端记录可以看到,客户端数量在28232时候不再增加了
客户端疯狂抛出异常 The Operation has timed out
上述原因是因为TcpClient服务器限制了端口范围
解决Linux端口范围限制
查看端口范围
#查看端口范围
sysctl net.ipv4.ip_local_port_range
可以看到端口范围在32768到60999之间
60999-32768=28231 正好印证了我们的猜想
修改端口范围
vim /etc/sysctl.conf
追加记录
net.ipv4.ip_local_port_range = 1024 65535
需要注意的是1024之前是系统保留端口,开始值不能小于1024,结束值不能大于65535否则会报错
保存后使用sysctl -p使修改立即生效
sysctl -p
再次使用命令sysctl net.ipv4.ip_local_port_range查看端口范围
sysctl net.ipv4.ip_local_port_range
可以看到刚才的设置成功了
失败尝试2(可用端口耗尽)
再次运行TcpServer:dotnet TcpServer.dll
再次运行TcpClient:dotnet TestTcpClient.dll
这次明显有了改善,从服务端记录可以看到,客户端数量在64511时候不再增加了
客户端依旧疯狂报错:the operation has timed out
这次的问题依旧在客户端,不过和上一次失败不太一样,上次是被Linux可用端口范围限制了,这次是真的把客户端端口耗尽了。
而我们的目标是单机10w连接,得想办法突破客户端65535端口数限制。
我们知道TCP协议由四元组组成,任何一个改变都视为一个新的连接:
<Server 端口><Client 端口>
所以我们有两种方案实现单机10W连接
方案一、改变 Server 端口
方案二、改变 Client IP
方案一、改变Server端口
当服务端监听7791端口时,客户端允许分配65535个端口建立连接
当服务端监听7792端口时,客户端将再次允许分配65535个端口建立连接
所以我们只需要对TcpServer代码进行改造增加监听端口即可.
修改TcpServer代码
TcpServer代码如下,监听7791 7792 7793三个端口
using System.Text; using TouchSocket.Core.Config; using TouchSocket.Core.Dependency; using TouchSocket.Core.Log; using TouchSocket.Sockets; namespace TcpServer { internal class Program { //用于记录客户端数量 private static int _count = 0; //阻塞事件,防止客户端退出 private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { TcpService service = new TcpService(); service.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"有客户端连接:{client.GetIPPort()} 目标端口:{client.ServicePort} ---- 客户端数量:{_count}"); }; service.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"有客户断开:{client.GetIPPort()} ---- 客户端数量:{_count}"); }; service.Setup( new TouchSocketConfig() .SetListenIPHosts(new IPHost[] {new IPHost(7791), new IPHost(7792), new IPHost(7793) }) .SetMaxCount(999999) //这里很重要,否则一分钟后服务端会删除无活动的连接 .SetClearInterval(-1)) .Start(); _closingEvent.WaitOne(); } } }
修改TcpClient代码
using System.Net.Sockets; using System.Text; using TouchSocket.Core.Config; using TouchSocket.Sockets; namespace TestTcpClient { internal class Program { //用于记录连接数量 private static int _count = 0; private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { for (int i = 1; i <= 120000; i++) { try { TouchSocket.Sockets.TcpClient tcpClient = new TouchSocket.Sockets.TcpClient(); tcpClient.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; tcpClient.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; if (i <= 40000) { tcpClient.Setup("1.14.107.247:7791"); } if (i > 40000) { tcpClient.Setup("1.14.107.247:7792"); } if (i > 80000) { tcpClient.Setup("1.14.107.247:7793"); } tcpClient.Connect(); } catch (Exception ex) { Console.WriteLine($"{i}:{ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"{i}:{ex.InnerException.Message}"); } } } _closingEvent.WaitOne(); } } }
再次测试(完成目标)
注意:如果您测试发现创建不了10w+连接,大概率是服务器内存满了,可以尝试加大服务器内存或者降低每个连接缓冲区大小
进阶测试(测试TCP连接上限)
服务端代码
using System.Text; using TouchSocket.Core.Config; using TouchSocket.Core.Dependency; using TouchSocket.Core.Log; using TouchSocket.Sockets; namespace TcpServer { internal class Program { //用于记录客户端数量 private static int _count = 0; //阻塞事件,防止客户端退出 private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { TcpService service = new TcpService(); service.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"有客户端连接:{client.GetIPPort()} 目标端口:{client.ServicePort} ---- 客户端数量:{_count}"); }; service.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"有客户断开:{client.GetIPPort()} ---- 客户端数量:{_count}"); }; service.Setup( new TouchSocketConfig() .SetListenIPHosts(new IPHost[] { new IPHost(7790), new IPHost(7791), new IPHost(7792), new IPHost(7793), new IPHost(7794), new IPHost(7795), new IPHost(7796), new IPHost(7797), new IPHost(7798), new IPHost(7799), new IPHost(7780), new IPHost(7781), new IPHost(7782), new IPHost(7783), new IPHost(7784), new IPHost(7785), new IPHost(7786), new IPHost(7787), new IPHost(7788), new IPHost(7789), }) .SetMaxCount(999999) //这里很重要,减少缓冲区大小防止把内存跑满了 .SetBufferLength(64) //这里很重要,否则一分钟后服务端会删除无活动的连接 .SetClearInterval(-1)) .Start(); _closingEvent.WaitOne(); } } }
客户端代码
using System.Net.Sockets; using System.Text; using TouchSocket.Core.Config; using TouchSocket.Sockets; namespace TestTcpClient { internal class Program { //用于记录连接数量 private static int _count = 0; private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { for (int i = 1; i <= 1000000; i++) { try { TouchSocket.Sockets.TcpClient tcpClient = new TouchSocket.Sockets.TcpClient(); tcpClient.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; tcpClient.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; //声明配置 TouchSocketConfig config = new TouchSocketConfig(); if (i <= 50000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7790")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 100000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7791")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 150000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7792")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 200000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7793")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 250000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7797")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 300000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7795")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 350000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7796")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 400000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7797")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 450000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7798")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 500000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7799")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 550000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7780")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 600000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7781")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 650000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7782")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 700000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7783")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 750000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7784")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 800000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7785")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 850000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7786")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 900000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7787")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 950000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7788")).SetBufferLength(64); tcpClient.Setup(config); } if (i > 1000000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7789")).SetBufferLength(64); tcpClient.Setup(config); } tcpClient.Connect(); } catch (Exception ex) { Console.WriteLine($"{i}:{ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"{i}:{ex.InnerException.Message}"); } } } _closingEvent.WaitOne(); } } }
测试结果,当前配置服务器极限大概能跑364510个TCP连接
查看内核对象发现使用率基本满了
slabtop
方案二、改变ClientIP
有时服务端受限于只能监听某一个端口,不能使用方案一。
此时我们可以给TcpCient服务器绑定多个外网IP解决65535端口数限制。
比如给TcpClient服务器绑定两个外网IP,并在TcpClient服务器创建两个网卡,不同网卡走不同外网IP。
此时TcpClient只需要前60000个请求走网卡1,后60000个请求走网卡二即可实现绕过65535端口数限制。接下来实操下。
腾讯云申请弹性网卡
参考链接:https://cloud.tencent.com/document/product/576/59353#ubuntu
腾讯云申请外网ip并绑定弹性网卡
修改TcpClient代码
在服务器查看配置好的IP和网卡
局域网IP分别是172.27.0.5和172.27.0.14
接下来修改TcpClient代码
using System.Net.Sockets; using System.Text; using TouchSocket.Core.Config; using TouchSocket.Sockets; namespace TestTcpClient { internal class Program { //用于记录连接数量 private static int _count = 0; private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false); static void Main(string[] args) { for (int i = 1; i <= 1000000; i++) { try { TouchSocket.Sockets.TcpClient tcpClient = new TouchSocket.Sockets.TcpClient(); tcpClient.Connected += (client, e) => { Interlocked.Increment(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; tcpClient.Disconnected += (client, e) => { Interlocked.Decrement(ref _count); Console.WriteLine($"当前连接数:{_count}"); }; TouchSocketConfig config = new TouchSocketConfig(); if (i <= 60000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7790")) .SetBufferLength(64) //通过网卡1建立连接 .SetBindIPHost("172.27.0.5:0"); tcpClient.Setup(config); } if (i > 60000) { config.SetRemoteIPHost(new IPHost("1.14.107.247:7790")) .SetBufferLength(64) //通过网卡2建立连接 .SetBindIPHost("172.27.0.14:0"); tcpClient.Setup(config); } tcpClient.Connect(); } catch (Exception ex) { Console.WriteLine($"{i}:{ex.Message}"); if (ex.InnerException != null) { Console.WriteLine($"{i}:{ex.InnerException.Message}"); } } } _closingEvent.WaitOne(); } } }
再次测试(完成目标)
结语
其他设置
查阅其他文章还提到修改下面的参数,
不过本文并没用到,先记录下。
vim /etc/sysctl.conf #系统最大句柄数 fs.file-max=1100000 #最大进程数 fs.nr_open=1100000 #使设置生效 sysctl -p vim /etc/security/limits.conf root soft nproc 1010000 root hard nproc 1010000 root soft nofile 1010000 root hard nofile 1010000
Windows为什么不行
假如将TcpClient放到本地windows下去连接服务端。
我们会发现建立3000个左右连接就无法继续创建了,打开网页也非常卡。
初步推测是路由器防局域网病毒之类的策略导致的,后续再研究。
源码下载:
链接:https://pan.baidu.com/s/1TdmFGxv_zCDp05PQMvGSrQ?pwd=8ajf
提取码:8ajf
参考资料:
https://blog.csdn.net/zhangyanfei01/article/details/114257045
https://mp.weixin.qq.com/s/fxy4S78Xw54y0iU_HsOosA
https://blog.51cto.com/u_15060546/2641200
https://blog.csdn.net/s_zhchluo/article/details/118415011