【QT性能优化】QT性能优化之QT6框架高性能网络编程框架实现百万TCP长连接网络服务器:QT是否适合做高性能网络应用?补天云这个视频告诉你在大厂Linux云服务器上的实测结果

QT性能优化之QT6框架高性能网络编程框架实现百万TCP长连接网络服务器

Ø 简介
本文作者编写了一套基于QT的TCP网络服务器程序和基于QT的TCP客户端程序,在某大厂的云服务器上进行了C1000K的实际测试。实测结果表明QT的网络模块足以支持开发常规业务的百万TCP长连接的高性能高并发网络服务器应用。整个测试程序主要使用了QT网络模块提供的QTcpServer和QTcpSocket这两个类型。

正文

Ø QT网络模块百万连接高性能高并发服务器应用实例效果

服务器配置

最近为了测试QT框架的网络模块在高性能高并发网络服务器应用领域的具体表现,搞了一台某大厂的云服务器,具体配置是64位CentOS8+16核CPU+64GB内存+50GB SSD+100MB外网网络带宽+10GB内网带宽;在服务器上安装了gcc/g++以及QT6.4。
可能有人会有困惑,使用这样配置的云服务器做这样的C1000K测试,是不是很贵呢?实际上云服务器一般的主机支持包月模式和按小时计费模式,网络流量支持固定带宽模式和按实际流量计费的模式。按小时计费模式也就是随用随开不用随时退还。本次测试过程持续几个小时花费了十几块RMB。

测试程序

服务器和客户端程序都是QT6开发的64位应用程序。服务器为一个独立进程工作在80端口。通过Bash Shell脚本启动了100个客户端进程,每一个进程与服务器建立了1万个连接。最后服务器承接了将近100万个TCP长连接。
每一个客户端建立与服务器的连接之后,每TCP连接每秒约发送一个约20字节的数据包之后开始接收服务器的回应数据,然后不断循环执行这个过程。为了避免占用过多CPU,也为了避免数据收发消耗掉过多内存,每发送一条数据等待1毫秒,而且每次只发送几十字节的数据。
服务器每次收到新的数据包之后,尽力对每一个数据包原封不动的发送到对应的客户端,相当于一个echo服务器。

实测效果

本文描叙实例对应的视频如下:

视频: QT性能优化之QT6框架高性能网络编程框架实现百万TCP长连接网络服务器效果

QT是否适合做高性能网络应用?补天云这个视频告诉你在大厂Linux云服务器上的实测结果:QT性能优化之QT6框架高性能网络编程框架实现百万TCP长连接网络服务器

在这里插入图片描述

QT网络模块高性能高并发网络服务器:100万TCP连接截图

在这里插入图片描述

QT网络模块高性能高并发网络服务器:80万TCP连接截图

在这里插入图片描述

QT网络模块高性能高并发网络服务器:1万TCP连接截图

为了避免在短时间内众多客户端瞬间建立太多TCP连接,在控制脚本和客户端代码中都加了一点延时操作。
实测统计结果表明服务器平均每秒接收和发送了各1MB数据。整体上相当于服务器每秒处理约5万个数据包,在应用层平均每秒接收1000到2000个新的TCP连接。
建立完成100个TCP连接,总耗时2小时30分,其中包括程序的延时和脚本的延时以及各种人工操作时的空闲等待时间。
QT网络模块提供的QTcpServer和QTcpSocket的事件分发机制的底层实现看起来似乎并非最优解。Windows版本使用Windows异步选择(AsyncSelect)消息机制,Linux版本使用Linux 投票(Poll)机制。但是就是这样的底层设计,实测仍然是通过了C1000K测试。

本次测试主要关注了QT网络服务器在Linux系统中是否能支持100万个TCP长连接。截图显示服务器在按照echo模式运行时收发数据包的字节数基本相等。体现了QT网络服务器可以实现一定程度的高并发性能和高性能。
本文作者还另外进行了QT网络服务器的数据吞吐量方面的测试,即使在Windows系统中在1万TCP长连接的情况下服务器也可实现100MB/秒到1GB/秒的吞吐量。体现了QT网络服务器可以实现一定程度的高性能和高吞吐量。

当然,不足之处在于网络延迟不是很好控制。

QT网络模块百万连接高性能高并发服务器应用实例源代码

服务器源代码

服务器主程序:

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

ButianyunTcpServer server;
server.listen(QHostAddress::AnyIPv4, 80);
qDebug() << QDateTime::currentDateTime().toString() << " Server is Ready.";

QTimer* timer = new QTimer(&a);
timer->setInterval(1000 * 10);
QObject::connect(timer, &QTimer::timeout,
timer, [&server] {
ButianyunTcpServerState stat = server.getServerState();
qDebug() << QDateTime::currentDateTime().toString()
<< " connection count:{" << stat.connection_count << "} "
<< " bytes read:{" << butianyun_bytes_human_readable(stat.bytes_read) << "} "
<< " bytes written:{" << butianyun_bytes_human_readable(stat.bytes_written) <<"} "
<< " error count:{" << stat.error_count << "}";
});
timer->start();

return a.exec();
}

服务器类型定义:

class ButianyunTcpConnection : public QTcpSocket
{
Q_OBJECT
public:
explicit ButianyunTcpConnection(QObject *parent = nullptr);

signals:
void sig_connection_info(long long read_bytes, long long write_bytes, bool error);

private slots:
void slot_connected();
void slot_readyRead();
void slot_bytesWritten(qint64 bytes);
void slot_disconnected();
void slot_errorOccurred(QAbstractSocket::SocketError error);
void slot_aboutToClose();


private:
QList<QByteArray> data_to_write;
QByteArray data_last_write;
};

服务器类型构造函数:

ButianyunTcpConnection::ButianyunTcpConnection(QObject *parent)
: QTcpSocket{parent}
{
connect(this, &ButianyunTcpConnection::connected,
this, &ButianyunTcpConnection::slot_connected);

connect(this, &ButianyunTcpConnection::readyRead,
this, &ButianyunTcpConnection::slot_readyRead);

connect(this, &ButianyunTcpConnection::bytesWritten,
this, &ButianyunTcpConnection::slot_bytesWritten);

connect(this, &ButianyunTcpConnection::disconnected,
this, &ButianyunTcpConnection::slot_disconnected);

connect(this, &ButianyunTcpConnection::errorOccurred,
this, &ButianyunTcpConnection::slot_errorOccurred);

connect(this, &ButianyunTcpConnection::aboutToClose,
this, &ButianyunTcpConnection::slot_aboutToClose);
}


² 客户端源代码

客户端的常数:

//一个客户端向服务器发起1万个连接。
#define BUTIANYUN_CONNECTION_COUNT_PER_CLIENT 10000
#define BUTIANYUN_CONNECTION_COUNT_PER_THREAD 1000
#define BUTIANYUN_THREAD_COUNT 10
#define BUTIANYUN_SOCKET_BUF_SIZE (1024 * 4)
#define BUTIANYUN_DATA_BYTES (10)

客户端主程序:

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
if (argc > 1)
{
remote_host = argv[1];
}

QThreadPool pool;
pool.setMaxThreadCount(BUTIANYUN_THREAD_COUNT);
for (int i = 0; i < BUTIANYUN_THREAD_COUNT; i++)
{
pool.start(butianyun_network_client);
}

pool.waitForDone();
qDebug() << "all connections exited.";

return a.exec();
}


客户端线程池中的线程函数:

static QAtomicInt connection_count = 0;
static QString remote_host = "127.0.0.1";


static void butianyun_network_client()
{
QThread::msleep(1000);
int connection_count_in_this_thread = 0;
QTcpSocket* clients = new QTcpSocket[BUTIANYUN_CONNECTION_COUNT_PER_THREAD];
for (int i = 0; i < BUTIANYUN_CONNECTION_COUNT_PER_THREAD; i++)
{
QThread::msleep(1);
clients[i].connectToHost(remote_host, 80);
clients[i].waitForConnected();
if (clients[i].state() == QTcpSocket::ConnectedState)
{
connection_count_in_this_thread++;
connection_count++;
}
clients[i].setSocketOption(QTcpSocket::KeepAliveOption, 1);
clients[i].setSocketOption(QTcpSocket::LowDelayOption, 1);
clients[i].setSocketOption(QTcpSocket::SendBufferSizeSocketOption, BUTIANYUN_SOCKET_BUF_SIZE);
clients[i].setSocketOption(QTcpSocket::ReceiveBufferSizeSocketOption, BUTIANYUN_SOCKET_BUF_SIZE);
}
qDebug() << "total " << connection_count << ", "
<< "this " << connection_count_in_this_thread << " connections.";

unsigned long long id = 0;
QByteArray bytes_read;
QString dummy;
dummy.resize(BUTIANYUN_DATA_BYTES);
dummy.fill(QChar('A'), BUTIANYUN_DATA_BYTES);

while (1)
{
long error_count = 0;
for (int i = 0; i < BUTIANYUN_CONNECTION_COUNT_PER_THREAD; i++)
{
id++;
QTcpSocket& client = clients[i];
if (client.state() == QTcpSocket::UnconnectedState)
{
error_count++;
continue;
}

QString msg = QString("%1:BuTianYun QT Network C1000K Test")
.arg(id);
msg += dummy;
client.write(msg.toUtf8());
client.waitForBytesWritten();
client.waitForReadyRead();
QByteArray bytes = client.readAll();
QThread::msleep(1);
}
if (error_count == BUTIANYUN_CONNECTION_COUNT_PER_THREAD)
{
qDebug() << "thread exited.";
break;
}
}
delete []clients;
}

QT网络模块的整体认识

QT网络模块对QT Widgets传统应用程序的支持

在这里插入图片描述

QT网络模块对传统QT Widgets应用的支持

QT网络模块对QML应用程序的支持

在这里插入图片描述

QT网络模块对QT QML应用的支持

QT网络模块套接字TCP SSL支持

QT对TCP 套接字C/S架构应用的支持

在这里插入图片描述

QT网络模块对传统的基于套接字的网络应用程序提供了支持。使用QTcpServer和QTcpSocket类型提供了对基于TCP的客户端服务器架构(C/S架构)的网络应用程序的支持。

QT TCP套接字C/S架构应用

QT对UDP应用的支持

QT网络模块提供了QUdpSocket类型提供了对Udp应用程序的支持。

在这里插入图片描述

QT UDP套接字应用

QT对Sctp应用的支持

QT网络模块提供了QSctpServer和QSctpSocket提供了对Sctp应用程序的支持。

QT对SSL应用的支持

QT网络模块提供了QSslServer和QSslSocket类型提供了对SSL套接字应用程序的支持。

QT对本地套接字的支持

QT网络模块提供了QLocalServer和QLocalSocket类型提供了对本地套接字应用程序的支持。

QT网络模块Web HTTP/HTTPS协议支持

QT对Web客户端应用的支持

QT网络模块提供了QNetworkAccessManager 、QNetworkRequest和QNetworkReply提供了对HTTP和HTTPS客户端应用程序的支持,使得我们可以直接使用QT提供的高层网络API编写HTTP/HTTPS客户端应用程序。比如用很少的代码就可以下载Web服务器上的网页数据,包括HTML/JSON/XML数据和二进制数据文件。另外,QT网络模块还提供了QHttpMultiPart类型以此支持 MIME multipart message。

在这里插入图片描述

QT对Web客户端应用的支持

QT对WebSocket应用的支持

QT网络模块提供了QWebSocketServer和QWebSocket类型提供对WebSocket服务器应用和WebSocket客户端应用的支持。说白了就是可以直接使用QT编写WebSocket服务器应用程序和WebSocket客户端应用程序,而无需关注WebSocket内部细节。

在这里插入图片描述

QT对WebSocket应用的支持

QT网络模块QML+AJAX+WebSocket支持

QT QML对AJAX应用的支持

下图是使用QT QML 提供的XMLHttpRequest对象执行HTTP POST操作的应用实例。

在这里插入图片描述

QT QML的XMLHttpRequest对HTTP POST的支持

下图是使用QT QML 提供的XMLHttpRequest对象执行HTTP GET操作的应用实例。

在这里插入图片描述

QT QML的XMLHttpRequest对HTTP GET的支持

QT QML对WebSocket应用的支持

QT QML提供了WebSocketServer和WebSocket这两个QML类型,以此支持使用QT QML开发WebSocket服务器应用和WebSocket客户端应用程序。
下图是QT QML开发的WebSocket服务器和WebSocket客户端的应用实例。

在这里插入图片描述

QT QML对WebSocket应用的支持

总结

本文介绍了在某大厂云服务器中使用QT网络模块提供的QTcpServer和QTcpSocket类型编写的C/S架构的网络应用成功通过了C1000K测试(即成功建立了100万个TCP长连接并能维持正常通讯)的实测情况和源代码。本文还简略介绍了QT网络模块所包含的各种网络相关功能的支持情况。

如果您认为这篇文章对您有所帮助,请您一定立即点赞+喜欢+收藏,本文作者将能从您的点赞+喜欢+收藏中获取到创作新的好文章的动力。如果您认为作者写的文章还有一些参考价值,您也可以关注这篇文章的作者。

posted @ 2023-07-14 11:29  QT界面美化性能优化  阅读(184)  评论(0编辑  收藏  举报  来源