一、前言
- 接下来几篇文章中将设计多线程的知识,本篇文章将使用C++11提供的多线程接口设计客户端程序,创建指定数量的客户端,然后将客户端的任务平均到每个线程中进行执行
- 注意:并不对之前的客户端实现代码EasyTcpClient.hpp做任何修改,是通过设计main()函数,设计多线程客户端测试程序
二、代码设计
- 代码设计主要的思想有:
- 创建一个UI线程,其线程执行函数为cmdThread(),用于输入命令退出程序
- 创建一个全局客户端数组client,其中存储客户端对象
- 为了将所有客户端的任务平均,我们设定了一个全局变量tCount,代表要创建多少个客户端数据处理线程
- 在main()中创建tCount数量的线程,每个线程中所操作的客户端数量平均,线程执行函数为sendThread()
- 因为创建的线程都是以detach()分离的,因此为了防止主程序比子线程提前退出,我们在main()的最后使用一个getchar()函数阻塞在程序中。如果想要终止程序,那么就回车,整个程序都终止
- 代码中的cCount和tCount可以修改,分别达到不同的效果。例如:
- cCount=100,tCount=4,代表创建4个线程,100个客户端,每个线程处理25个客户端
- cCount=1000,tCount=10,代表创建10个线程,1000个客户端,每个线程处理100个客户端
#include "EasyTcpClient.hpp"
#include <thread>
bool g_bRun = false;
const int cCount = 100; //客户端的数量
const int tCount = 4; //线程的数量
EasyTcpClient* client[cCount];//客户端的数组
void cmdThread();
void sendThread(int id);
int main()
{
g_bRun = true;
//UI线程
std::thread t(cmdThread);
t.detach();
//启动发送线程
for (int n = 0; n < tCount; ++n)
{
std::thread t(sendThread, n + 1);
t.detach();
}
std::cout << "客户端停止工作!" << std::endl;
getchar();
return 0;
}
void cmdThread()
{
char cmdBuf[256] = {};
while (true)
{
std::cin >> cmdBuf;
if (0 == strcmp(cmdBuf, "exit"))
{
g_bRun = false;
break;
}
else {
std::cout << "命令不识别,请重新输入" << std::endl;
}
}
}
void sendThread(int id)
{
/*
下面这几个变量是为了平均每个线程创建的客户端的数量:
例如,本次测试时客户端数量为1000,线程数量为4,那么每个线程应该创建250个客户端
线程1:c=250,begin=0,end=250
线程2:c=250,begin=250,end=500
线程3:c=250,begin=500,end=750
线程4:c=250,begin=750,end=1000
*/
int c = cCount / tCount;
int begin = (id - 1)*c;
int end = id*c;
for (int n = begin; n < end; ++n) //创建客户端
{
client[n] = new EasyTcpClient;
}
for (int n = begin; n < end; ++n) //让每个客户端连接服务器
{
client[n]->ConnectServer("192.168.0.105", 4567);
printf("Connect=%d\n", n);
}
Login login;
strcpy(login.userName, "dongshao");
strcpy(login.PassWord, "123456");
//循环向服务端发送消息
while (g_bRun)
{
for (int n = begin; n < end; ++n)
{
client[n]->SendData(&login);
//client[n]->Onrun();
}
}
//关闭客户端
for (int n = begin; n < end; ++n)
{
client[n]->CloseSocket();
}
}
三、测试4线程100客户端连接
- 客户端测试程序就是上面的代码:
- cCount代表客户端数量,为100
- tCount代表线程数量,为4
- 因此每个线程处理25个客户端的数据发送
- 服务端测试程序如下:
#include "EasyTcpServer.hpp"
#include "MessageHeader.hpp"
int main()
{
EasyTcpServer server;
server.Bind("192.168.0.105", 4567);
server.Listen(5);
while (server.isRun())
{
server.Onrun();
}
server.CloseSocket();
std::cout << "服务端停止工作!" << std::endl;
getchar(); //防止程序一闪而过
return 0;
}
- 测试结果如下图所示:
- 左侧为服务端,右侧为客户端
- 服务端每秒接收到的数据包数量大概在10万左右
- 资源监视器如下图所示:
- 可以看到网络总带宽大概1Gbps
- 可以看到服务端接收到的数据字节数为一亿多,与上图相符合(上图中服务端每秒收到10万左右数据包,每个数据包为1000KB,因此相乘就是一千多万)
四、测试4线程1000客户端连接
- 客户端测试程序就是上面的代码:
- cCount代表客户端数量,修改为1000即可
- tCount代表线程数量,仍然为4
- 因此每个线程处理250个客户端的数据发送
- 服务端测试程序同上
- 测试结果如下图所示:
- 左侧为服务端,右侧为客户端
- 服务端每秒接收到的数据包数量大概在3万左右
- 资源监视器如下图所示:
- 可以看到网络总带宽大概700Mbps
- 可以看到服务端接收到的数据字节数为三千多万左右,与上图相符合(上图中服务端每秒收到3万左右数据包,每个数据包为1000KB,因此相乘就是三千多万)