Qt實現UDP通信的示例代碼
設想有如下場景:若干的客戶端與服務器端建立連接,建立連接后,服務器端隨機發(fā)送字符串給客戶端,客戶端打印輸出。該節(jié)案例使用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. 創(chuàng)建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. 創(chuàng)建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%左右。
通過閱讀服務器端,發(fā)現單線程處理客戶端的連接效率較低。服務器端可修改為多線程處理客戶端連接,代碼如下:
服務器端-多線程
頭文件
#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通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++深入探究哈希表如何封裝出unordered_set和unordered_map
哈希表是一種根據關鍵碼去尋找值的數據映射結構,該結構通過把關鍵碼映射的位置去尋找存放值的地方,說起來可能感覺有點復雜,我想我舉個例子你就會明白了,最典型的的例子就是字典2022-06-06C/C++中static,const,inline三種關鍵字詳細總結
以下是對C/C++中static,const,inline的三種關鍵字進行了詳細的分析介紹,需要的朋友可以過來參考下2013-09-09c語言獲取用戶輸入字符串是scanf和gets的區(qū)別詳解
今天小編就為大家分享一篇c語言獲取用戶輸入字符串是scanf和gets的區(qū)別詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07