1. 程式人生 > >Qt下的Tcp協議練習——客戶端

Qt下的Tcp協議練習——客戶端

上一篇文章記錄了本次聊天室專案服務端的實現過程和主要程式碼,本篇文章將記錄客戶端的實現過程和主要程式碼。

至於Tcp的原理,上篇文章已經提到過了,所以在這裡就不多囉嗦了。

還是一樣的套路,上來先初始化控制元件物件和設定佈局,這些都不是重點,可以自己隨意佈局。

    //設定視窗標題
    setWindowTitle(tr("TCP Client"));
    //初始化控制元件物件
    contentListWidget = new QListWidget;
    userNameLabel = new QLabel(tr("name:"));
    userNameLineEdit = new QLineEdit;
    serverIPLabel = new QLabel(tr("IP:"));
    serverIPLineEdit = new QLineEdit;
    portLabel = new QLabel(tr("port:"));
    portLineEdit = new QLineEdit;
    enterBtn = new QPushButton(tr("Enter the chat room!"));
    sendLineEdit = new QLineEdit;
    sendBtn = new QPushButton(tr("Send!"));
    mainLayout = new QGridLayout(this);
    //設定佈局
    mainLayout->addWidget(userNameLabel,0,0);
    mainLayout->addWidget(userNameLineEdit,0,1);
    mainLayout->addWidget(serverIPLabel,1,0);
    mainLayout->addWidget(serverIPLineEdit,1,1);
    mainLayout->addWidget(portLabel,2,0);
    mainLayout->addWidget(portLineEdit,2,1);
    mainLayout->addWidget(enterBtn,3,0,1,2);
    mainLayout->addWidget(contentListWidget,4,0,1,2);
    mainLayout->addWidget(sendLineEdit,5,0);
    mainLayout->addWidget(sendBtn,5,1);

至於執行出來的效果呢,就是下面這個樣子,有點醜陋,哈哈。

接下來就是一些關鍵函式的實現了,我會把一些說明寫到程式碼的註釋當中

    //記錄當前狀態,true表示已經進入聊天室,false表示不在聊天室中,根據status的值判斷是進入還是離開聊天室
    status = false;
    //指定埠
    port = 80;
    //將埠顯示到控制元件上
    portLineEdit->setText(QString::number(port));
    //初始化記錄IP地址的物件
    serverIP = new QHostAddress();
    //將按鈕的點選訊號和想要對應的槽函式連線起來
    connect(enterBtn,SIGNAL(clicked(bool)),this,SLOT(slotEnter()));
    connect(sendBtn,SIGNAL(clicked(bool)),this,SLOT(slotSend()));
    //將傳送資訊按鈕設定為不可點選狀態
    sendBtn->setEnabled(false);

接下來實現兩個槽函式

首先是加入enterBtn對應的加入聊天室的槽函式

這段程式碼有點長,主要還是按照當前是否已經連線到聊天室的兩種情況分別進行處理

slotEnter():

    if(!status){//判斷當前狀態,是否已經加入到聊天室中
        //獲取控制元件上輸入的IP地址
        QString ip = serverIPLineEdit->text();

        if(!serverIP->setAddress(ip)){//判斷獲取的IP地址是否能夠正常被解析
            //如果不能,則彈出警告
            QMessageBox::information(this,tr("error"),tr("ip error!"));
            return;
        }
        if(userNameLineEdit->text()==""){//判斷控制元件中的使用者名稱是否為空
            //如果使用者名稱為空,則發出警告
            QMessageBox::information(this,tr("error"),tr("userName error!"));
            return;
        }
        //將控制元件中的使用者名稱儲存到之前定義的變數當中
        userName = userNameLineEdit->text();
        //初始化Tcp套接字,並各個訊號和對應的槽函式連線起來
        tcpSocket = new QTcpSocket(this);
        connect(tcpSocket,SIGNAL(connected()),this,SLOT(slotConnected()));
        connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
        connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
        //與TCP服務端進行連線,成功的話會發送connected()訊號
        tcpSocket->connectToHost(*serverIP,port);
        //記錄當前連線狀態,記錄為已進入聊天室
        status = true;
    }else{//如果已經連線到了服務端,執行面下的操作
        //定義一個變數,用來儲存資訊的長度
        int length = 0;
        //定義一條離開聊天室的資訊,離開聊天室時要傳送這條資訊給服務端
        QString msg = userName+tr(":Leave chat Room!");
        //通知服務端,我要離開聊天室了,也就是傳送上面那條資訊給服務端
        //如果傳送成功,則會返回資訊的長度,通過這種方式來判斷,資訊是否傳送出去了
        if((length = tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length()){
            return;
        }
        //與服務端斷開連線後,會發出disconnected()訊號
        tcpSocket->disconnectFromHost();
        //儲存當前狀態,記錄為已經離開聊天室
        status = false;
    }

當要連線服務端的時候傳送connected()訊號,觸發slotConnected()槽函式,下面是槽函式的實現

slotConnected():

    //將傳送資訊按鈕設定為可點選狀態
    sendBtn->setEnabled(true);
    //將加入聊天室按鈕的文字資訊更改為leave
    enterBtn->setText(tr("Leave!"));
    //傳送訊息給服務端,通知服務端我要加入聊天室了,具體流程跟上面傳送資訊的流程相同,這裡就不重複說明了
    int length = 0;
    QString msg = userName+tr(":Enter chat Room!");
    if((length = tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length()){
        return;
    }

當與服務端斷開連線的時候傳送disconnected()訊號,觸發slotDisconnected()槽函式,程式碼如下

slotDisconnected():

    //既然已經離開聊天室了,傳送資訊的按鈕當然也就不能點啦
    sendBtn->setEnabled(false);
    //然後進入聊天室按鈕的資訊也要修改一下
    enterBtn->setText(tr("Enter the chat room!"));

當有資料到來時,就會觸發dataReceived()槽函式,從套接字中取出有效資料並顯示出來

dataReceived():

    //迴圈條件是等待讀取的位元組數大於0,也就是說還有資料沒有讀出來
    while(tcpSocket->bytesAvailable()>0){
        QByteArray datagram;
        //統一一下長度
        datagram.resize(tcpSocket->bytesAvailable());
        //開始讀取有效資料
        tcpSocket->read(datagram.data(),datagram.size());
        //把讀取到的資料轉化為QString型別,方便顯示
        QString msg = datagram.data();
        //把訊息顯示到控制元件上就ok了
        contentListWidget->addItem(msg.left(datagram.size()));
    }

最後要實現的是點擊發送訊息按鈕時觸發的槽函式slotSend()

slotSend():

    if(sendLineEdit->text()==""){//判斷輸入框中是否輸入了訊息
        return;
    }
    //把你的使用者名稱拼接到訊息前面,讓其他使用者知道是誰傳送的訊息
    QString msg = userName+":"+sendLineEdit->text();
    //開始向套接字中寫入資料
    tcpSocket->write(msg.toLatin1(),msg.length());
    //最後,訊息都發送出去了,肯定要把輸入框裡面的文字清空掉啦
    sendLineEdit->clear();

就這樣,本次做的練習就結束了,這次練習主要是熟悉Tcp協議連線,斷開連線,讀取訊息,傳送訊息的流程和實現方法。

這次練習之後會進行一段時間的吸收,後續可能會做一個融入更多自己理解的案例,如果有時間的話,哈哈,但是學習的步伐是不會停下來的,希望各位大佬能夠多多指教,多多和我交流。