Linux下Qtcpserver總結(jié)(基于QT的嵌入式聊天室)

期末大作業(yè)是要做一個(gè)網(wǎng)絡(luò)聊天室,于是學(xué)習(xí)了一個(gè)開源的項(xiàng)目,里面能夠?qū)W習(xí)的東西還是蠻多的,在這里做一下總結(jié)。

項(xiàng)目地址

Group6ChatRoom

QT環(huán)境配置

嵌入式項(xiàng)目,要求程序可以在Linux上面可以跑,選擇使用Qt,C++畢竟是面向?qū)ο蟮木幊陶Z言,用來封裝方法還是很方便的。
系統(tǒng)使用的是deepin,深度商店還是比較方便的直接下載Qt Creator,環(huán)境配置比GTK+省略了不少東西。
項(xiàng)目.pro中添加support

# 對網(wǎng)絡(luò)請求必須加上network
QT       += core gui network

QTcpServer用法總結(jié)

頭函數(shù)需要添加庫文件

#include <QTcpSocket>
#include <QTcpServer>

槽函數(shù)也是自己定義,連接和斷開時(shí)候的事件。
這里是傳入線程指針,server端接受到client連接請求時(shí)候的socket描述符,會為對應(yīng)的描述符創(chuàng)建對應(yīng)線程去處理這個(gè)請求。

    void on_client_connected(ClientThread* clientThread);
    void on_client_disconnected(ClientThread* clientThread);

頭文件中聲明

protected:
    //重新實(shí)現(xiàn)該方法用來接受client,并創(chuàng)建線程處理該連接
    void incomingConnection(qintptr socketDescriptor) override;

該方法在代碼中實(shí)現(xiàn),為

 void Server::incomingConnection(qintptr socketDescriptor){
    qDebug() << socketDescriptor << " 連接中...";
    QTcpSocket *socket = new QTcpSocket();
    ClientThread *cliThread = new ClientThread(socketDescriptor,socket,this);

    //為連接進(jìn)來的線程綁定事件
    connect(cliThread, SIGNAL(finished()),cliThread,SLOT(deleteLater()));
    connect(cliThread, SIGNAL(connected(ClientThread*)),this,SIGNAL(connected(ClientThread*)));
    connect(cliThread, SIGNAL(connected(ClientThread*)),this,SLOT(on_client_connected(ClientThread*)));
    ·
    ·
    ·
    connect(cliThread, SIGNAL(clientDisconnected(ClientThread*)),this,SIGNAL(clientDisconnected(ClientThread*)));
   

    //將socket轉(zhuǎn)移到線程處理
    socket->moveToThread(cliThread);
    cliThread->start(); //啟用線程
}

服務(wù)端開啟監(jiān)聽,等待連接

使用QTcpServer的異步模式,需要覆蓋其中的一個(gè)listen函數(shù),在調(diào)用listen()相當(dāng)于開啟一個(gè)循環(huán)(不會調(diào)用waitForNewConnection()一種阻塞方法或者叫做同步),關(guān)鍵代碼。

//默認(rèn)使用host即“127.0.0.1”
    //調(diào)用listen函數(shù),在指定的端口號中進(jìn)行任意監(jiān)聽 ps:Any參數(shù)表示任意IPv4地址0.0.0.0
    if(!this->listen(QHostAddress::Any,port)){
        qDebug() << "無法啟動服務(wù)端!";//log
        return false;
    }
    else{
        qDebug() << "server 監(jiān)聽中...";//log
        return true;
    }

該方法需要設(shè)置ip以及端口號,使用函數(shù)獲取或者手動輸入也可以。

客戶端連接到服務(wù)端

關(guān)鍵代碼,試圖連接到主機(jī)host的指定端口 并立即return。

//獲取地址
    QHostAddress servAddr(serverIP);
    ·
    ·
    //設(shè)置ip和端口號
    connectToHost(servAddr,this->port);
    waitForConnected(30000); //設(shè)置超時(shí)時(shí)間為30秒.

QTcpSocket數(shù)據(jù)傳送

qt中的該類提供了一個(gè)tcp套接字,面向傳輸,面向連接的可靠的傳輸協(xié)議。

/** 將消息發(fā)送給所有線程*/
void Server::sendTextToAll(QString text,ClientThread* except){
    //用于暫存要發(fā)送的數(shù)據(jù)
    QByteArray block;
    //使用數(shù)據(jù)流寫入數(shù)據(jù) 只寫(設(shè)置為ReadWrite則為讀寫)
    QDataStream out(&block,QIODevice::WriteOnly);
    //設(shè)置數(shù)據(jù)流的版本,客戶端server與服務(wù)端Client版本需要相同
    out.setVersion(QDataStream::Qt_5_8);

    //設(shè)置初始值為0,設(shè)置長度
    out << (quint32)0 << text;

    //回到字節(jié)流的起始位置
    out.device()->seek(0);
    //重置字節(jié)流長度
    out << (quint32)(block.size() - sizeof(quint32));
    qDebug() << "block.size() = " << block.size();//調(diào)試用(打印出block的長度)

    qint64 x = 0;
    //遍歷線程列表
    foreach(ClientThread* eachClient,clientThreadList){
        if(except != NULL && eachClient == except)
            continue;
        x = 0;
        while(x < block.size()){
            //向套接字緩存中寫入數(shù)據(jù)(主要)
            qint64 y = eachClient->getTcpSocket()->write(block);
            x+=y;
            //緩沖池在首次連接的時(shí)候沒有數(shù)據(jù),在首次連接成功時(shí)候打印出發(fā)送者
            qDebug() << eachClient->getUsername()<< "/sent" << x ;//調(diào)試用(輸出發(fā)送者)
        }
        qDebug() << "-----";
    }
}

有關(guān)正則消息驗(yàn)證

client 與 server 兩端是分離的,可以開啟多個(gè)client端,每一端發(fā)送的數(shù)據(jù)都要經(jīng)過server的接收處理,分組轉(zhuǎn)發(fā)。所以這里的思路是,所有向server端發(fā)送數(shù)據(jù)時(shí)候,對應(yīng)的數(shù)據(jù)類型在Qstring字符串上進(jìn)行處理(比如前面加上特定的字符:/pm就代表一條私聊消息),之后server在經(jīng)過正則驗(yàn)證,不同的數(shù)據(jù)觸發(fā)不同的信號。
總結(jié)一下capture(QRegularExpression類使用正則表達(dá)式提供模式匹配)

        //私法消息的格式
        QString psersonal="/pm:"+currentTime+myUsername+text
        //私人消息的驗(yàn)證
        QRegularExpression regex_private("^/pm:(.*)/(.*) : (.*)\n$");
        ·
        ·
        //
        QString senderName = match.captured(2);
        QString time = match.captured(1);
        QString text = match.captured(3);

分別提取對應(yīng)括號中的文本信息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容