網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
設(shè)想有如下場(chǎng)景:若干的客戶端與服務(wù)器端建立連接,建立連接后,服務(wù)器端隨機(jī)發(fā)送字符串給客戶端,客戶端打印輸出。該節(jié)案例使用TCP編程。
服務(wù)器端-單線程
頭文件
#pragma once
//
//tcp服務(wù)端-單線程處理客戶端連接
#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()
{
//初始換原始數(shù)據(jù)
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對(duì)象
m_pTcpServer = new QTcpServer(this);
//2. 新連接、錯(cuò)誤信號(hào)
connect(m_pTcpServer, &QTcpServer::newConnection, this, &SimpleTcpSocketServerDemo::sendData);
connect(m_pTcpServer, &QTcpServer::acceptError, this, &SimpleTcpSocketServerDemo::displayError);
//3. 啟動(dòng)服務(wù)端
if (!m_pTcpServer->listen(QHostAddress::Any, 8888))
{
qDebug() << "m_pTcpServer->listen() error";
assert(false);
}
}
void SimpleTcpSocketServerDemo::sendData()
{
//獲取服務(wù)端數(shù)據(jù)
QString sWriteData = m_oData.at(qrand() % m_oData.size());
//獲取與客戶端通信的socket
QTcpSocket* pClientConnection = m_pTcpServer->nextPendingConnection();
//從客戶端讀數(shù)據(jù)
QString sReadData = pClientConnection->readAll();
qDebug() << "SimpleTcpSocketServerDemo::readDataFromClient " << pClientConnection;
//與客戶端寫(xiě)數(shù)據(jù)
qDebug() << "SimpleTcpSocketServerDemo::writeDataToClient " << sWriteData;
pClientConnection->write(sWriteData.toUtf8());
// //與客戶端斷開(kāi)連接
// connect(pClientConnection, &QTcpSocket::disconnected, this, &SimpleTcpSocketServerDemo::deleteLater);
// pClientConnection->disconnectFromHost();
}
void SimpleTcpSocketServerDemo::displayError(QAbstractSocket::SocketError socketError)
{
qDebug() << "SimpleTcpSocketServerDemo::displayError " << socketError;
}
void testSimpleTcpSocketServerDemo()
{
//這樣寫(xiě)會(huì)內(nèi)存泄漏,如此寫(xiě)方便測(cè)試。
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套接字對(duì)象
m_pTcpSocket = new QTcpSocket(this);
//2. 已連接、數(shù)據(jù)可讀、失敗信號(hào)連接
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. 與服務(wù)器端建立連接
m_pTcpSocket->connectToHost("127.0.0.1", 8888);
//4. 同步處理-等待數(shù)據(jù)可讀
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()
{
//這樣寫(xiě)會(huì)內(nèi)存泄漏,如此寫(xiě)方便測(cè)試。
SimpleTcpSocketClientDemo* pSimpleTcpSocketClient = new SimpleTcpSocketClientDemo;
}
#define CLINET_COUNT 2000 //客戶端的數(shù)量
void testSimpleTcpSocketClientDemo()
{
QTime oTime;
oTime.start();
//同步線程池的方式模擬多個(gè)客戶端與服務(wù)器端交互
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";
}
測(cè)試結(jié)果-單線程
服務(wù)器端
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
通過(guò)測(cè)試輸出,可以看到服務(wù)器端與客戶端建立了正確的連接并且數(shù)據(jù)交換。
– 實(shí)際測(cè)試數(shù)據(jù):2000個(gè)連接,耗時(shí)4s左右,CPU使用率10%左右。
通過(guò)閱讀服務(wù)器端,發(fā)現(xiàn)單線程處理客戶端的連接效率較低。服務(wù)器端可修改為多線程處理客戶端連接,代碼如下:
服務(wù)器端-多線程
頭文件
#pragma once
//
//服務(wù)器端-多線程處理客戶端連接
#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); //該虛函數(shù)是重點(diǎn)
源文件
#include "MultiThreadTcpSocketServerDemo.h"
#include <QDebug>
#include <QTcpSocket>
MultiThreadTcpSocketServerDemo::MultiThreadTcpSocketServerDemo()
{
//初始換原始數(shù)據(jù)
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)
{
//獲取服務(wù)端數(shù)據(jù)
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. 向客戶端寫(xiě)數(shù)據(jù)
qDebug() << "MultiThreadTcpSocketServerDemo::readDataFromClient" << &oTcpSocket;
qDebug() << "MultiThreadTcpSocketServerDemo::writeDataToClient" << m_sWriteData;
oTcpSocket.write(m_sWriteData.toUtf8());
oTcpSocket.disconnectFromHost();
oTcpSocket.waitForDisconnected();
}
void testMultiThreadTcpSocketServerDemo()
{
//1. 建立服務(wù)器端套接字
MultiThreadTcpSocketServerDemo* m_pTcpServer = new MultiThreadTcpSocketServerDemo();
//2. 啟動(dòng)服務(wù)端
if (!m_pTcpServer->listen(QHostAddress::Any, 8888))
{
qDebug() << "m_pTcpServer->listen() error";
}
}
測(cè)試結(jié)果-多線程
客戶端
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
– 實(shí)際測(cè)試數(shù)據(jù):2000個(gè)連接,耗時(shí)6.5s左右,CPU使用率20%左右。
可見(jiàn)服務(wù)器端采用多線程可充分利用CPU,但是頻繁的切換線程也會(huì)性能下降(耗時(shí))。
通過(guò)本案例的代碼實(shí)現(xiàn)可以了解TCP服務(wù)器端/客戶端編程的基本思路。并且驗(yàn)證了服務(wù)器端單線程和多線程的效率對(duì)比。 在windows中,可通過(guò)IOCP提高服務(wù)期端的效率,后面會(huì)詳細(xì)講解。
原文鏈接:https://blog.csdn.net/m0_60259116/article/details/128104519
相關(guān)推薦
- 2022-07-23 .Net創(chuàng)建型設(shè)計(jì)模式之簡(jiǎn)單工廠模式(Simple?Factory)_基礎(chǔ)應(yīng)用
- 2023-03-18 C#調(diào)用dll報(bào)錯(cuò):無(wú)法加載dll,找不到指定模塊的解決_C#教程
- 2022-03-25 在?ASP.NET?Core?中為?gRPC?服務(wù)添加全局異常處理_ASP.NET
- 2022-03-31 jenkins?實(shí)現(xiàn)shell腳本化定時(shí)執(zhí)行任務(wù)的方法_linux shell
- 2022-06-01 c++?深入理解歸并排序的用法_C 語(yǔ)言
- 2022-09-06 C語(yǔ)言單鏈表遍歷與求和示例解讀_C 語(yǔ)言
- 2023-05-06 Python中dilb和face_recognition第三方包安裝失敗的解決_python
- 2022-10-03 Objective-C優(yōu)雅使用KVO觀察屬性值變化_IOS
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支