Qt搭建多線程Server


起因是MySQL在Android上沒有驅動。也就是說,移動端想要訪問遠程數據庫,必須通過一台(或多台)PC進行中轉。

中轉PC作為Server,接受來自移動端Socket訪問數據庫的要求,Server訪問數據庫,取得數據,通過Socket發送給移動端。

Qt寫個C/S其實很簡單,網上各種教程,硬傷:Server!是!單!線!程!

假設有10000個移動端訪問中轉Server,那么如果Server是單線程,那么這10000個移動端是排隊通信,排隊訪問數據庫,肯定完蛋!

所以Server必須使用多線程。

Qt的多線程是個經常讓新手搞錯的東西,很多文章中看起來是多線程,實際上根本就是單線程。

默認的C/S連接方式(acceptConnection)不支持多線程也是硬傷!

於是搞了好久,總算搞定了多線程Server。

 

 

①首先寫Server類,派生自QTcpServer, 只要重載 incomingConnection 這個虛函數就行了。

無須像單線程那樣  connect(&server,SIGNAL(newConnection()),this,SLOT(acceptConnection()));

void Server::incomingConnection (qintptr socketDescriptor)
{
    SocketThread *thread = new SocketThread(socketDescriptor,this);
    Processor *cpu=new Processor(thread->socket);
    connect(thread->socket,SIGNAL(readyRead()),cpu,SLOT(work()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    cpu->moveToThread (thread);
    thread->start();
}

在這個函數里有幾個陌生的玩意。

SocketThread類 派生自QThread,子線程不解釋。

Processor類 派生自 QObject,這個類是重點。

Qt多線程的最大問題在於,除了子線程的run函數是跑在子線程里,線程其它函數(包括信號/槽)都是跑在主線程里。

我們的Server肯定要處理Client的請求,也就是Socket的數據請求,在Qt里,這步被封裝在Socket的readyRead信號里。

就算你在run函數里綁了readyRead信號,最后信號還是會在主線程里觸發。

解決方案是單獨寫個處理類,這里就是Processor類,將子線程moveThread到這個對象中,這樣這個對象的所有函數都是在子線程里執行了,work函數用於Server接受請求以及返回數據庫數據。

這是Qt 4.7之后,官方的推薦寫法,因為N多人的多線程寫的根本就是錯的,官方實在忍不了了。  

 

②再看 SocketThread類

class SocketThread : public QThread
{
    Q_OBJECT
public:
    SocketThread(int socketDescriptor,QObject* parent);
    int socketDescriptor;
    QTcpSocket *socket;
    void run();
};



SocketThread::SocketThread(int id,QObject *parent):QThread()
{
    socketDescriptor=id;
    socket=new QTcpSocket;
}
void SocketThread::run ()
{
    socket->setSocketDescriptor(socketDescriptor);
    QThread::run ();
}
Thread

構造函數不用多說,傳入系統為Server分配的Socket的識別id。

關鍵就是QThread的虛函數run。首先設置Server的Socket識別id。

記得調用 QThread::run ();  否則這個run函數並沒有完全執行。

 

③再看Processor類

void Processor::work ()
{
    //qDebug()<<"當前線程: "<<QThread::currentThreadId ()<<endl;
    buff=m_socket->readAll ();
    m_socket->write (buff);
    //qDebug()<<buff<<endl;
}
Processor

成員就一個指針m_socket,保存子線程的socket地址。

以及一個QByteArray作為緩沖區buff。

readAll讀取Client的Socket,write寫回Client的Sokcet。

無聊的話可以把注釋拿掉,看看work函數的工作線程是否與主線程不同。

 

④Client端

void Client::send ()
{
    socket->connectToHost(QHostAddress(address->text ()),7399);
    QString x="2333,要完蛋了 "+QString::number (cnt++);
    socket->write(x.toStdString ().c_str ());
}
void Client::get ()
{
    QString data=socket->readAll();
    qDebug()<<"接收端:"<<data<<endl;
    socket->disconnectFromHost ();
}

這里使用的策略如下:

每發一個請求,連一次Server,收到Server的回復后,斷開連接,防止占用Sever資源。

get函數作為Client的readyRead的槽函數就行了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM