Qt5 編寫網絡調試助手


QT中可以通過TCP協議讓服務器和客戶端之間行通信。所以下面我就圍繞服務器和客戶端來寫。

這是我們寫服務器和客戶端的具體流程:

A、服務器:      

         1.創建QTcpServer對象
         2.啟動服務器(監聽)調用成員方法listen(QHostAddress::Any,端口號)
         3.當有客戶端鏈接時候會發送newConnection信號,觸發槽函數接受鏈接(得到一個與客戶端通信的套接字QTcpSocket)
         4.QTcpsocket發送數據用成員方法write,
         5.讀數據當客戶端有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據

B、客戶端  :  

        1.創建QTcpSocket對象
        2.鏈接服務器connectToHost(QHostAddress("ip"),端口號)
        3.QTcpsocket發送數據用成員方法write,
        4.讀數據當對方有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據

我們需要調用到的頭文件有兩個:

#include <QTcpServer>  
#include <QTcpSocket>

我們先要在工程文件中加入network

QT       += core gui network

下面我們來看看服務器程序步驟:

1、初始化服務器server對象

mServer = new QTcpServer();

2、啟動監聽服務器

  mServer->listen(QHostAddress::Any,9988);//9988為端口號

3、當有客戶端鏈接時候會發送newConnection信號,觸發槽函數接受鏈接(得到一個與客戶端通信的套接字QTcpSocket)

 connect(mServer,SIGNAL(newConnection()),this,SLOT(new_client()));
 mSocket = mServer->nextPendingConnection();//與客戶端通信的套接字

4、發送數據

 mSocket->write(msg.toUtf8());

5、讀數據當客戶端有數據來,QTcpSocket對象就會發送readyRead信號,關聯槽函數讀取數據

 connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_client_data()));

6、連接多個客戶端

 //可以實現同時讀取多個客戶端發送過來的消息
 QTcpSocket *obj = (QTcpSocket*)sender();

7、檢測掉線

 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis())); //檢測掉線信號

下面是服務器的實現的具體代碼:

復制代碼
 1 #include "tcpserver.h"
 2 #include "ui_tcpserver.h"
 3 #include <QDebug>
 4 TcpServer::TcpServer(QWidget *parent) :
 5     QMainWindow(parent),
 6     ui(new Ui::TcpServer)
 7 {
 8     ui->setupUi(this);
 9     //初始化服務器server對象
10     mServer = new QTcpServer();
11     //關聯客戶端連接信號newConnection
12     connect(mServer,SIGNAL(newConnection()),this,SLOT(new_client())); //連接客戶端
13     //啟動服務器監聽
14     mServer->listen(QHostAddress::Any,9988);
15 
16 }
17 
18 TcpServer::~TcpServer()
19 {
20     delete ui;
21 }
22 
23 void TcpServer::new_client()
24 {
25     qDebug()<<"新客戶段連接";
26     mSocket = mServer->nextPendingConnection();//與客戶端通信的套接字
27     //關聯接收客戶端數據信號readyRead信號(客戶端有數據就會發readyRead信號)
28     connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_client_data()));
29     //檢測掉線信號
30     connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis()));
31 
32 }
33 
34 void TcpServer::read_client_data()
35 {
36     //可以實現同時讀取多個客戶端發送過來的消息
37     QTcpSocket *obj = (QTcpSocket*)sender();
38     QString msg = obj->readAll();
39     qDebug()<<msg;
40 }
41 
42 void TcpServer::client_dis()
43 {
44      QTcpSocket *obj = (QTcpSocket*)sender();//掉線對象
45      qDebug()<<obj->peerAddress().toString();//打印出掉線對象的ip
46 }
復制代碼

 

說完服務器那我們繼續來看看客戶端是怎么實現的:

1、創建QTcpSocket對象

 mSocket = new QTcpSocket();

2、鏈接服務器connectToHost(QHostAddress("ip"),端口號),連接服務器ip和端口號

 mSocket->connectToHost(ui->ipEdit->text(),ui->portEdit->text().toInt()); //ui->ipEdit->text():ip,ui->portEdit->text().toInt():端口號

3、發送數據

//取發送信息編輯框內容
QString msg = ui->sendEdit->toPlainText();
mSocket->write(msg.toUtf8());//轉編碼

4、檢測鏈接成功信號關聯槽函數

 connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc()));

5、檢測掉線信號

 connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis()));

6、服務器和客戶端關閉都可以使用close

 mSocket->close();

這是客戶端實現的具體代碼

復制代碼
 1 #include "tcpclient.h"
 2 #include "ui_tcpclient.h"
 3 #include <QDebug>
 4 TcpClient::TcpClient(QWidget *parent) :
 5     QMainWindow(parent),
 6     ui(new Ui::TcpClient)
 7 {
 8     ui->setupUi(this);
 9     //初始化套接字對象
10     mSocket = new QTcpSocket();
11     //關聯數據信號
12     connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_data()));
13 
14 }
15 
16 TcpClient::~TcpClient()
17 {
18     delete ui;
19 }
20 
21 void TcpClient::read_data()
22 {
23     QString msg = mSocket->readAll();
24     qDebug()<<msg;
25 }
26 
27 void TcpClient::on_btn_connectServer_clicked()
28 {
29     //檢測鏈接成功信號關聯槽函數
30     connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc()));
31     //檢測掉線信號
32     connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_dis()));
33     //連接服務器,設置ip和端口號
34     mSocket->connectToHost(ui->ipEdit->text(),ui->portEdit->text().toInt());
35 
36 }
37 
38 void TcpClient::on_btn_send_clicked()
39 {
40     //取發送信息編輯框內容
41     QString msg = ui->sendEdit->toPlainText();
42     mSocket->write(msg.toUtf8());//轉編碼
43 }
44 
45 void TcpClient::connect_suc()
46 {
47     ui->btn_connectServer->setEnabled(false);//如果連接成功則連接按鈕不能按下
48 }
49 void TcpClient::client_dis()
50 {
51     ui->btn_connectServer->setEnabled(true);//如果連接沒有成功則連接按鈕還可以按下
52 }
復制代碼

 

這是服務器和客戶端分開兩個文件夾寫的程序,在這里我也實現了服務器和客戶端寫在同一個文件中

具體代碼如下:

頭文件:tcpapp.h

#ifndef TCPAPP_H
#define TCPAPP_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QFile>
#include <QTimer>
#include <QMessageBox>
namespace Ui {
class TcpApp;
}

class TcpApp : public QMainWindow
{
    Q_OBJECT

public:
    explicit TcpApp(QWidget *parent = 0);
    ~TcpApp();

private slots:
    void on_severRB_clicked();//選擇作為服務器

    void on_clientRB_clicked();//選擇作為客戶端

    void on_StartBt_clicked();//啟動服務器或鏈接客戶端

    void on_closeBt_clicked();//關閉服務器或斷開客戶端

    void on_onlineUserList_doubleClicked(const QModelIndex &index);//選擇給哪個客戶端發送數據

    void on_autoCB_clicked(bool checked);//選擇自動發送還是手動發送

    void on_sendMsgBt_clicked();//發送信息

    //服務器
    void accept_connect();//與newconnection信號關聯
    void recv_data(); //接收數據

     void auto_time_send();//定時器定時發送數據

     void client_disconnect();//關聯掉線信號
     void connect_suc();//檢測客戶端連接成功信號

     void on_clearRcvBt_clicked();

     void on_clearSendBt_clicked();

private:
    Ui::TcpApp *ui;
    QTimer *mTimer;//定時發送數據
    QTcpServer *mServer;
    QTcpSocket *mSocket;
    QVector<QTcpSocket*> clients; //存儲所有在線客戶端(容器)

    bool isServer;//標志位,true為服務器,false為客戶端

    //保存接收和發送數據的字節數
    quint64 recvSize;
    quint64 sendSize;

    qint16 onNum;
    bool isCheckServer;//判斷是否選擇了服務器
    bool isCheckClient;//判斷是否選擇了客戶端


};

#endif // TCPAPP_H

源文件:tcpapp.cpp

#include "tcpapp.h"
#include "ui_tcpapp.h"

TcpApp::TcpApp(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::TcpApp),
    onNum(0)
{
    ui->setupUi(this);
    recvSize = 0;
    sendSize = 0;
    //初始化定時器
    mTimer = new QTimer();
    connect(mTimer,SIGNAL(timeout()),this,SLOT(auto_time_send()));
}

TcpApp::~TcpApp()
{
    delete ui;
}

//與newconnection信號關聯
void TcpApp::accept_connect()
{
    mSocket = mServer->nextPendingConnection(); //返回與客戶端連接通信的套接字

    //關聯接收數據信號
    connect(mSocket,SIGNAL(readyRead()),this,SLOT(recv_data()));
    //關聯掉線信號
    connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_disconnect()));

    //上線用戶添加到客戶列表容器
    clients.append(mSocket);
    //把用戶添加到界面列表中
    QString ip = mSocket->peerAddress().toString().remove("::ffff:");//去除客戶端中多余的字符
    ui->onlineUserList->addItem(ip);

    //在線數量添加
    onNum++;
    ui->onlineUserCount->setText(QString::number(onNum));//顯示在線數

}

//接收數據
void  TcpApp::recv_data()
{
      QTcpSocket *obj = (QTcpSocket*)sender();
      //獲取發送數據端的IP
      QString ip = obj->peerAddress().toString();
      ip.remove("::ffff:");
      QString msg = obj->readAll();
      ui->receiveList->addItem(ip+":"+msg);//顯示接收到的數據
      recvSize += msg.size();//統計接收到的數據的字節數
      ui->receiveNumLabel->setText(QString::number(recvSize));
}

void TcpApp::client_disconnect()
{
    QTcpSocket *obj = (QTcpSocket*)sender();//獲取掉線對象
    if(isServer)
    {
        int row = clients.indexOf(obj);//找到掉線對象的內容所在的行
        QListWidgetItem *item = ui->onlineUserList->takeItem(row);//從界面列表中去除找到的一行內容
        delete item;
        clients.remove(row);//從容器中刪除對象

        //掉線時刪除在線數量
        onNum--;
        ui->onlineUserCount->setText(QString::number(onNum));
    }
    else
    {
        ui->StartBt->setEnabled(true);//斷開連接的時候重新啟用開始按鈕
    }
}


//客戶端連接成功
void TcpApp::connect_suc()
{
    ui->StartBt->setEnabled(false);//連接成功則禁用開始按鈕
}
//定時器定時發送數據
void TcpApp::auto_time_send()
{
    quint64 len = mSocket->write(ui->sendMsgEdit->toPlainText().toUtf8());
    if(len > 0)
    {
        sendSize += len;//統計發送的字節數
        ui->sendNumLabel->setText(QString::number(sendSize));//把發送的字節數顯示到sendNumLabel上

    }
}

//選擇作為服務器
void TcpApp::on_severRB_clicked()
{
    this->isCheckServer = true;
    this->isServer = true;
    //獲取本地ip顯示在IpEdit中
   ui->IpEdit->setText(QHostAddress(QHostAddress::LocalHost).toString());
   ui->IpEdit->setEnabled(false);//關閉ip輸入編輯器
   this->isCheckClient = false;

}

//選擇作為客戶端
void TcpApp::on_clientRB_clicked()
{
    this->isCheckClient = true;
    this->isServer = false;
    ui->IpEdit->setEnabled(true);//打開ip輸入編輯器
    this->isCheckServer = false;

}

//啟動服務器或者鏈接服務器
void TcpApp::on_StartBt_clicked()
{
    if(isServer) //服務器
    {
        mServer = new QTcpServer();
        //關聯新客戶端鏈接信號
        connect(mServer,SIGNAL(newConnection()),this,SLOT(accept_connect()));
        mServer->listen(QHostAddress::Any,ui->PortEdit->text().toInt());//啟動服務器監聽
        ui->StartBt->setEnabled(false);//開始按鈕禁用
    }
    if(isServer == false) //客戶端
    {
        mSocket = new QTcpSocket();
        //檢測鏈接成功信號
        connect(mSocket,SIGNAL(connected()),this,SLOT(connect_suc()));
        //設置服務器的 ip和端口號
        mSocket->connectToHost(ui->IpEdit->text(),ui->PortEdit->text().toInt());


        //關聯接收數據信號
        connect(mSocket,SIGNAL(readyRead()),this,SLOT(recv_data()));
        //關聯掉線信號
        connect(mSocket,SIGNAL(disconnected()),this,SLOT(client_disconnect()));
    }

    if(isCheckServer == false && isCheckClient == false)//如果兩個都沒選擇
    {
        QMessageBox::warning(this,"提示","請選擇服務器或者客戶端");
        ui->StartBt->setEnabled(true);
        return;
    }

    if(isCheckServer)//選擇了服務器
    {
       if(ui->PortEdit->text().isEmpty() || ui->PortEdit->text() == "請輸入端口號")
       {
           QMessageBox::warning(this,"提示","請輸入端口號");
           ui->StartBt->setEnabled(true);
           return;
       }
    }

    if(isCheckClient)//選擇了客戶端
    {
        if(ui->IpEdit->text().isEmpty() || ui->IpEdit->text() == "請輸入ip" || ui->IpEdit->text() == "請輸入端口號")
        {
            QMessageBox::warning(this,"提示","請輸入ip和端口號");
            ui->StartBt->setEnabled(true);
            return;
        }
    }

}

//關閉服務器或者斷開
void TcpApp::on_closeBt_clicked()
{
    if(isServer)//服務器
    {
        for(int i=0;i<clients.count();i++)
        {
             clients.at(i)->close();//關閉所有客戶端
        }

        //關閉所有服務器之后開始按鈕才能啟用
        mServer->close();
        ui->StartBt->setEnabled(true);
    }
    else //客戶端
    {
        mSocket->close();//關閉客戶端
        ui->StartBt->setEnabled(true);//啟用開始按鈕
    }

}

//雙擊選擇要發送的客戶端
void TcpApp::on_onlineUserList_doubleClicked(const QModelIndex &index)
{
    mSocket = clients.at(index.row());

}

//自動發送數據
void TcpApp::on_autoCB_clicked(bool checked)
{
    if(checked)

    {
        if(ui->autoTimeEdit->text().toInt() <= 0)
        {
            QMessageBox::warning(this,"提示","請輸入時間值ms");
            ui->autoCB->setChecked(false);//把按鈕重新置於沒選中的狀態
            return;
        }
        mTimer->start(ui->autoTimeEdit->text().toInt());//啟動定時器
    }
    else
    {
        mTimer->stop();//停止定時器
    }

}

//手動發送數據
void TcpApp::on_sendMsgBt_clicked()
{
    auto_time_send();

}

//清空接收區
void TcpApp::on_clearRcvBt_clicked()
{
    ui->receiveNumLabel->clear();
    this->recvSize = 0;
    ui->receiveNumLabel->setText(QString::number(recvSize));
}

//清空發送區
void TcpApp::on_clearSendBt_clicked()
{
    ui->sendNumLabel->clear();
    this->sendSize = 0;
    ui->sendNumLabel->setText(QString::number(sendSize));
}

界面文件tcpapp.ui如下圖

 

 

此外這里還使用到了容器,在這里講講容器的使用

1、定義容器對象

QVector<QTcpSocket*> clients; //存儲所有在線客戶端(容器)
  解釋:QTcpSocke*  容器的類型
       clients  容器名

2、往容器中添加成員

//上線用戶添加到客戶列表容器
 clients.append(mSocket); 

3、尋找某個成員在容器中位置

 int row = clients.indexOf(obj);//找到掉線對象的內容所在的行

4、從容器中刪除成員

  clients.remove(row);//從容器中刪除成員

 

 


免責聲明!

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



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