Qt实现UDP通信的示例代码
目录
- 服务器端-单线程
- 客户端
- 测试结果-单线程
- 服务器端-多线程
- 测试结果-多线程
设想有如下场景:若干的客户端与服务器端建立连接,建立连接后,服务器端随机发送字符串给客户端,客户端打印输出。该节案例使用TCP编程。
服务器端-单线程
头文件
#pragma once // //tcp服务端-单线程处理客户端连接 #include <QAbstractSocket> #include <QObject> class QTcpServer; class SimpleTcpSocketServerDemo : public QObject { Q_OBJECT public: SimpleTcpSocketServerDemo(); private slots: void sendData(); void displayError(QAbstractSocket::SocketError socketError); private: QStringList m_oData; QTcpServer *m_pTcpServer; }; void testSimpleTcpSocketServerDemo();
源文件
#include "SimpleTcpSocketServerDemo.h" #include <assert.h> #include <QTcpServer> #include <QTcpSocket> #include <QDebug> #include <QDataStream> SimpleTcpSocketServerDemo::SimpleTcpSocketServerDemo() { //初始换原始数据 m_oData << tr("You've been leading a dog's life. Stay off the furniture.") << tr("You've got to think about tomorrow.") << tr("You will be surprised by a loud noise.") << tr("You will feel hungry again in another hour.") << tr("You might have mail.") << tr("You cannot kill time without injuring eternity.") << tr("Computers are not intelligent. They only think they are."); //1. 创建TCP对象 m_pTcpServer = new QTcpServer(this); //2. 新连接、错误信号 connect(m_pTcpServer, &QTcpServer::newConnection, this, &SimpleTcpSocketServerDemo::sendData); connect(m_pTcpServer, &QTcpServer::acceptError, this, &SimpleTcpSocketServerDemo::displayError); //3. 启动服务端 if (!m_pTcpServer->listen(QHostAddress::Any, 8888)) { qDebug() << "m_pTcpServer->listen() error"; assert(false); } } void SimpleTcpSocketServerDemo::sendData() { //获取服务端数据 QString sWriteData = m_oData.at(qrand() % m_oData.size()); //获取与客户端通信的socket QTcpSocket* pClientConnection = m_pTcpServer->nextPendingConnection(); //从客户端读数据 QString sReadData = pClientConnection->readAll(); qDebug() << "SimpleTcpSocketServerDemo::readDataFromClient " << pClientConnection; //与客户端写数据 qDebug() << "SimpleTcpSocketServerDemo::writeDataToClient " << sWriteData; pClientConnection->write(sWriteData.toUtf8()); // //与客户端断开连接 // connect(pClientConnection, &QTcpSocket::disconnected, this, &SimpleTcpSocketServerDemo::deleteLater); // pClientConnection->disconnectFromHost(); } void SimpleTcpSocketServerDemo::displayError(QAbstractSocket::SocketError socketError) { qDebug() << "SimpleTcpSocketServerDemo::displayError " << socketError; } void testSimpleTcpSocketServerDemo() { //这样写会内存泄漏,如此写方便测试。 SimpleTcpSocketServerDemo* pSimpleTcpSocketServer = new SimpleTcpSocketServerDemo; }
客户端
头文件
#pragma once // //客户端 #include <QObject> #include <QAbstractSocket> #include <QRunnable> #include <QThreadPool> class QTcpSocket; class SimpleTcpSocketClientDemo : public QObject { Q_OBJECT public: SimpleTcpSocketClientDemo(); private slots: void connected(); void readyRead(); void error(QAbstractSocket::SocketError socketError); private: QTcpSocket* m_pTcpSocket; }; class ClientRunnable : public QRunnable { public: void run(); }; void testSimpleTcpSocketClientDemo();
源文件
#include "SimpleTcpSocketClientDemo.h" #include <QTcpSocket> #include <QDebug> SimpleTcpSocketClientDemo::SimpleTcpSocketClientDemo() { //1. 创建TCP套接字对象 m_pTcpSocket = new QTcpSocket(this); //2. 已连接、数据可读、失败信号连接 connect(m_pTcpSocket, &QTcpSocket::connected, this, &SimpleTcpSocketClientDemo::connected); connect(m_pTcpSocket, &QIODevice::readyRead, this, &SimpleTcpSocketClientDemo::readyRead); typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError); connect(m_pTcpSocket, static_cast<QAbstractSocketErrorSignal>(&QTcpSocket::error), this, &SimpleTcpSocketClientDemo::error); //3. 与服务器端建立连接 m_pTcpSocket->connectToHost("127.0.0.1", 8888); //4. 同步处理-等待数据可读 m_pTcpSocket->waitForReadyRead(); } void SimpleTcpSocketClientDemo::readyRead() { qDebug() << "SimpleTcpSocketClientDemo::readyRead " << m_pTcpSocket->readAll(); } void SimpleTcpSocketClientDemo::connected() { qDebug() << "SimpleTcpSocketClientDemo::connected successfully"; } void SimpleTcpSocketClientDemo::error(QAbstractSocket::SocketError socketError) { qDebug() << "SimpleTcpSocketClientDemo::error " << socketError; } void ClientRunnable::run() { //这样写会内存泄漏,如此写方便测试。 SimpleTcpSocketClientDemo* pSimpleTcpSocketClient = new SimpleTcpSocketClientDemo; } #define CLINET_COUNT 2000 //客户端的数量 void testSimpleTcpSocketClientDemo() { QTime oTime; oTime.start(); //同步线程池的方式模拟多个客户端与服务器端交互 for (int nIndex = 0; nIndex < CLINET_COUNT; ++nIndex) { ClientRunnable* pRunnable = new ClientRunnable; pRunnable->setAutoDelete(false); QThreadPool::globalInstance()->start(pRunnable); } QThreadPool::globalInstance()->waitForDone(30 * 1000); qDebug() << "connect count: " << CLINET_COUNT << "total time: " << (double)oTime.elapsed() / double(1000) << "s"; }
测试结果-单线程
服务器端
SimpleTcpSocketServerDemo::readDataFromClient QTcpSocket(0x2f27f308)
SimpleTcpSocketServerDemo::writeDataToClient "You will feel hungry again in another hour."
SimpleTcpSocketServerDemo::readDataFromClient QTcpSocket(0x2eb61cf0)
SimpleTcpSocketServerDemo::writeDataToClient "You might have mail."
.........
客户端
SimpleTcpSocketClientDemo::connected successfully
SimpleTcpSocketClientDemo::readyRead "You might have mail."
SimpleTcpSocketClientDemo::connected successfully
SimpleTcpSocketClientDemo::readyRead "You will feel hungry again in another hour."
.........
connect count: 2000 total time: 3.926 s
通过测试输出,可以看到服务器端与客户端建立了正确的连接并且数据交换。
– 实际测试数据:2000个连接,耗时4s左右,CPU使用率10%左右。
通过阅读服务器端,发现单线程处理客户端的连接效率较低。服务器端可修改为多线程处理客户端连接,代码如下:
服务器端-多线程
头文件
#pragma once // //服务器端-多线程处理客户端连接 #include <QTcpServer> #include <QThread> class MultiThreadTcpSocketServerDemo : public QTcpServer { public: MultiThreadTcpSocketServerDemo(); //This virtual function is called by QTcpServer when a new connection is available. //The socketDescriptor argument is the native socket descriptor for the accepted connection. virtual void incomingConnection(qintptr handle); private: QStringList m_oData; }; //处理线程 class ServerHandleThread : public QThread { Q_OBJECT public: ServerHandleThread(qintptr handle, const QString& sWriteData); virtual void run(); private: qintptr m_nHandle; QString m_sWriteData; }; void testMultiThreadTcpSocketServerDemo();
//This virtual function is called by QTcpServer when a new connection is available. //The socketDescriptor argument is the native socket descriptor for the accepted connection. virtual void incomingConnection(qintptr handle); //该虚函数是重点
源文件
#include "MultiThreadTcpSocketServerDemo.h" #include <QDebug> #include <QTcpSocket> MultiThreadTcpSocketServerDemo::MultiThreadTcpSocketServerDemo() { //初始换原始数据 m_oData << tr("You've been leading a dog's life. Stay off the furniture.") << tr("You've got to think about tomorrow.") << tr("You will be surprised by a loud noise.") << tr("You will feel hungry again in another hour.") << tr("You might have mail.") << tr("You cannot kill time without injuring eternity.") << tr("Computers are not intelligent. They only think they are."); } void MultiThreadTcpSocketServerDemo::incomingConnection(qintptr handle) { //获取服务端数据 QString sWriteData = m_oData.at(qrand() % m_oData.size()); qDebug() << "MultiThreadTcpSocketServerDemo::incomingConnection" << handle; ServerHandleThread* pThread = new ServerHandleThread(handle, sWriteData); connect(pThread, &ServerHandleThread::finished, pThread, &ServerHandleThread::deleteLater); pThread->start(); } ServerHandleThread::ServerHandleThread(qintptr handle, const QString& sWriteData) :m_sWriteData(sWriteData), m_nHandle(handle) { } void ServerHandleThread::run() { //1. 建立与客户端通信的TCP套接字 QTcpSocket oTcpSocket; if (!oTcpSocket.setSocketDescriptor(m_nHandle)) { qDebug() << "oTcpSocket.setSocketDescriptor error"; return; } //2. 向客户端写数据 qDebug() << "MultiThreadTcpSocketServerDemo::readDataFromClient" << &oTcpSocket; qDebug() << "MultiThreadTcpSocketServerDemo::writeDataToClient" << m_sWriteData; oTcpSocket.write(m_sWriteData.toUtf8()); oTcpSocket.disconnectFromHost(); oTcpSocket.waitForDisconnected(); } void testMultiThreadTcpSocketServerDemo() { //1. 建立服务器端套接字 MultiThreadTcpSocketServerDemo* m_pTcpServer = new MultiThreadTcpSocketServerDemo(); //2. 启动服务端 if (!m_pTcpServer->listen(QHostAddress::Any, 8888)) { qDebug() << "m_pTcpServer->listen() error"; } }
测试结果-多线程
客户端
SimpleTcpSocketClientDemo::connected successfully
SimpleTcpSocketClientDemo::readyRead "You might have mail."
SimpleTcpSocketClientDemo::connected successfully
SimpleTcpSocketClientDemo::readyRead "You will feel hungry again in another hour."
.........
connect count: 2000 total time: 6.403 s
– 实际测试数据:2000个连接,耗时6.5s左右,CPU使用率20%左右。
可见服务器端采用多线程可充分利用CPU,但是频繁的切换线程也会性能下降(耗时)。
通过本案例的代码实现可以了解TCP服务器端/客户端编程的基本思路。并且验证了服务器端单线程和多线程的效率对比。 在windows中,可通过IOCP提高服务期端的效率,后面会详细讲解。
到此这篇关于Qt实现UDP通信的示例代码的文章就介绍到这了,更多相关Qt UDP通信内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!