49.Qt-網絡編程之QTCPSocket和QTCPServer(實現簡易網絡調試助手)

8.9更新說明html

以下圖所示,支持十六進制收發,下載地址已經更新.源碼下載地址:https://download.csdn.net/download/qq_37997682/11504836服務器

 


 

在上章 48.QT-網絡通訊講解1,咱們學習了網絡通訊基礎後,本章便來實戰一篇.網絡

PS:支持客戶端和服務器,提供源碼,而且服務器支持多客戶端連入,而且能夠指定與個別客戶端發送數據,也能夠給全部連入的客戶端發送數據.socket

1.效果圖所下所示:tcp

 

以下圖所示,當服務器狀態下,若是有客戶端連入,會提示客戶端信息:ide

 

2.效果操做函數

客戶端操做:post

 

服務器操做:學習

從上面操做能夠看出,服務器支持多客戶端連入,而且能夠指定與個別客戶端發送數據,也能夠給全部連入的客戶端發送數據.ui

3.首先建立UI

 

4.注意事項

無論是服務器仍是客戶端,均可以經過peerAddress()和peerPort()來獲取目標地址和目標端口

4.1服務器監聽時

好比服務器,則能夠經過QTcpSocket的peerAddress()則能夠獲取連入的客戶端地址

也能夠經過children()來獲取全部連入的客戶端(須要注意的是也會獲取到服務器自己的tcp地址和端口),示例以下:

QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
foreach (QTcpSocket *tcp, m_tcps)
{
        qDebug() << "Address:" << tcp->peerAddress ();
        qDebug() << "Port:" << tcp->peerPort  ();
}

若是咱們只向連入的客戶端某個端口發送數據時,就能夠經過上面的方式篩選出來.

這樣作的話若是以爲很麻煩,也能夠將以前鏈接上的客戶端存到QList裏再進行篩選.

4.2 QTcpSocket步驟

  • 首先經過connectToHost()來鏈接服務器.
  • 而後調用waitForConnected()來判斷是否鏈接服務器超時
  • 當咱們接收到服務器數據的時候,則會發出readyRead()信號,而後再進行read ()讀取發來的數據
  • 發送數據時,則調用write()函數進行發送,當bytesWritten()信號函數觸發時,即可以獲取成功發送的數據長度.

注意:若是read到的數據長度量不是本身想要的,此時咱們即可以經過bytesAvailable()來讀取接收到的數據長度量.當達到多少時,再進行read ()讀取.

 

4.3 QTcpServer步驟

  • 首先經過listen(QHostAddress::AnyIPv4, port)來監聽全部來自IPV4的客戶端
  • 當有新的客戶端鏈接服務器的時候,會自動觸發newConnection()信號函數,而後咱們能夠經過經過QTcpSocket * nextPendingConnection()成員函數來獲取當前鏈接上的新的客戶端類.而後再對QTcpSocket來進行信號槽綁定
  • 當客戶端發來數據的時候,則能夠經過咱們定義的onServerDataReady()來讀取數據
  • 當咱們向某個鏈接的客戶端發送數據時,則經過m_server.findChildren()來篩選出來,而後write便可.

 

5.代碼介紹

5.1  頭文件介紹

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QTcpServer>
#include <QMessageBox>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

    QTcpSocket m_client;
    QTcpServer m_server;
    QString targetAddr;
    int targetPort;
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private slots:
    void on_btn_switch_clicked();
    void on_tcpMode_currentIndexChanged(int index);
    void on_btn_send_clicked();             //發送按鈕
    void on_betn_clear_clicked();           //清空按鈕

    //客戶端槽函數
    void onClientConnected();
    void onClientDisconnected();
    void onClientDataReady();
    void onClientBytesWritten(qint64 bytes);
    void onClientErr(QAbstractSocket::SocketError socketError);

    //服務器槽函數
    void onServerNewConnection();
    void onServerConnected();
    void onServerDisconnected();
    void onServerDataReady();
    void onServerBytesWritten(qint64 bytes);
private:
    void startConnect(bool ison);

    void initClientSignals();   //初始化客戶端信號槽
    bool startClient();         //啓動客戶端

    void initServerSignals();   //初始化客戶端信號槽
    bool startServer();     //啓動服務器

    Ui::Widget *ui;
};

#endif // WIDGET_H

5.2  widget.cpp介紹

該cpp主要是用來處理界面操做的函數

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    startConnect(false);
    on_tcpMode_currentIndexChanged(0);
    initClientSignals();        //初始化客戶端信號槽
    initServerSignals();        //初始化服務器信號槽

    //限制只能數字輸入
    QRegExp regx("[0-9]+$");
    QValidator *validator = new QRegExpValidator(regx, this );
    ui->ipAddr1->setValidator( validator );
    ui->ipAddr2->setValidator( validator );
    ui->ipAddr3->setValidator( validator );
    ui->ipAddr4->setValidator( validator );
    ui->ipPort->setValidator( validator );
}
void Widget::on_tcpMode_currentIndexChanged(int index)
{
    if(index==0)        //clent
    {
        ui->ipPortLabel->setText("服務器端口號:");
        ui->ipAddLabel->show();
        ui->ipAdds->show();
        ui->targetLabel->hide();
        ui->targetObject->hide();
    }
    else
    {
        ui->ipAddLabel->hide();
        ui->ipAdds->hide();
        ui->ipPortLabel->setText("本地端口號:");
    }
}

void Widget::on_btn_switch_clicked()        //切換鏈接開關
{
    bool ret;
    if(ui->btn_switch->text()=="打開鏈接")
    {
        if(ui->tcpMode->currentIndex()==0)  //啓動客戶端
           ret=startClient() ;
        else
            ret=startServer();
        if(ret)
            startConnect(true);
    }
    else
    {
        if(ui->tcpMode->currentIndex()==0)  //啓動客戶端
          m_client.close();
        else
        {
            if( m_server.isListening() )
            {
                QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
                foreach (QTcpSocket *tcp, m_tcps)
                {
                    tcp->close();
                }
                m_server.close();
            }
        }
        startConnect(false);
    }
}


void Widget::startConnect(bool ison)
{
    if(!ison)
    {
        ui->btn_switch->setStyleSheet("color:blue;border: 1px solid blue");
        ui->btn_switch->setText("打開鏈接");

        //使能
        ui->ipAddr1->setEnabled(true);
        ui->ipAddr2->setEnabled(true);
        ui->ipAddr3->setEnabled(true);
        ui->ipAddr4->setEnabled(true);
        ui->tcpMode->setEnabled(true);
        ui->ipPort->setEnabled(true);
        ui->localPort->setText("");
    }
    else
    {
        ui->btn_switch->setStyleSheet("color:red;border: 1px solid red");
        ui->btn_switch->setText("關閉鏈接");
        //失能
        ui->ipAddr1->setEnabled(false);
        ui->ipAddr2->setEnabled(false);
        ui->ipAddr3->setEnabled(false);
        ui->ipAddr4->setEnabled(false);
        ui->tcpMode->setEnabled(false);
        ui->ipPort->setEnabled(false);
        targetAddr="";
        targetPort=0;
        ui->sendLenLabel->setText("0");
    }
}

void Widget::on_betn_clear_clicked()
{
   ui->recvEdit->clear();
   targetAddr="";
   targetPort=0;
}

void Widget::on_btn_send_clicked()
{
    if(ui->btn_switch->text()!="打開鏈接")
    {
        if(ui->tcpMode->currentIndex()==0)  //客戶端
        {
            m_client.write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
        }
        else
        {
            if(ui->targetObject->currentText()!="全部對象")
            {
                QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
                foreach (QTcpSocket *tcp, m_tcps)
                {
                    if(ui->targetObject->currentText() == tcp->objectName())
                    {
                        tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
                        break;
                    }
                }
            }
            else            //全部鏈接上的客戶端都發送一遍
            {
                QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
                foreach (QTcpSocket *tcp, m_tcps)
                {
                    tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
                }
            }
        }
    }
}

Widget::~Widget()
{
    QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
    foreach (QTcpSocket *tcp, m_tcps)
    {
        tcp->close();
    }
    if(m_client.isOpen())
    {
        m_client.close();
         qDebug()<<"m_client close";
    }
    delete ui;
}

5.3 clentHandler.cpp介紹

該cpp主要用來處理客戶端操做相關的文件.

#include "widget.h"
#include "ui_widget.h"

void Widget::initClientSignals()  //初始化客戶端信號槽
{
    connect(&m_client, SIGNAL(connected()), this, SLOT(onClientConnected()));
    connect(&m_client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
    connect(&m_client, SIGNAL(readyRead()), this, SLOT(onClientDataReady()));
    connect(&m_client, SIGNAL(bytesWritten(qint64)), this, SLOT(onClientBytesWritten(qint64)));
    connect(&m_client, SIGNAL(error(QAbstractSocket::SocketError )), this, SLOT(onClientErr(QAbstractSocket::SocketError)));
}
bool Widget::startClient()         //啓動客戶端
{
    QString ip = QString("%1.%2.%3.%4").arg(ui->ipAddr1->text()).arg(ui->ipAddr2->text()).arg(ui->ipAddr3->text()).arg(ui->ipAddr4->text());
    qDebug()<<ip;
    m_client.connectToHost(ip, ui->ipPort->text().toInt());
    if(m_client.waitForConnected(800))
    {
        return true;
    }
    else
    {
        QMessageBox::information(this,"提示",QString("鏈接超時"),QMessageBox::Ok);
        return false;
    }
}

void Widget::onClientConnected()
{
    startConnect(true);
    QMessageBox::information(this,"提示","鏈接成功",QMessageBox::Ok);
    ui->localPort->setText(QString("%1").arg(m_client.localPort())); //顯示本地端口號
}
void Widget::onClientDisconnected()
{
    startConnect(false);
    QMessageBox::information(this,"提示","斷開完成",QMessageBox::Ok);
}
void Widget::onClientDataReady()
{
    if(m_client.peerAddress().toString()!=targetAddr || m_client.peerPort()!=targetPort  )
    {
        targetAddr = m_client.peerAddress().toString();
        targetPort = m_client.peerPort();
        ui->recvEdit->insertPlainText("[接受來自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n");
    }
    ui->recvEdit->moveCursor(QTextCursor::End);
    ui->recvEdit->insertPlainText(QString::fromLocal8Bit(m_client.readAll())+"\r\n");
}
void Widget::onClientBytesWritten(qint64 bytes)
{
    qDebug() << "onBytesWritten:" << bytes;
    ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));
}

void  Widget::onClientErr(QAbstractSocket::SocketError socketError)
{
    qDebug()<<"onClientErr:"<<socketError;
    m_client.close();
    startConnect(false);
    QMessageBox::information(this,"提示",QString("鏈接失敗:%1").arg((int)socketError),QMessageBox::Ok);
}

5.4 serverHandler.cpp介紹

該cpp主要用來處理服務器操做相關的文件

#include "widget.h"
#include "ui_widget.h"

void Widget::initServerSignals()   //初始化信號槽
{
    connect(&m_server, SIGNAL(newConnection()), this, SLOT(onServerNewConnection()));
}
bool Widget::startServer()         //啓動服務器
{
    if(m_server.listen(QHostAddress::AnyIPv4,ui->ipPort->text().toInt()))       //只監聽IPV4的全部客戶端
    {
        ui->targetLabel->show();
        ui->targetObject->show();
        ui->localPort->setText(QString("%1").arg(m_server.serverPort()));
        return true;
    }
    else
       return false;
}
void Widget::onServerNewConnection()
{
    qDebug() << "onNewConnection";
    QTcpSocket* tcp = m_server.nextPendingConnection();     //獲取新的客戶端信息
    QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort());
    ui->targetObject->addItem(info);
    QMessageBox::information(this,"提示",QString("新的客戶端連入:%1").arg(info),QMessageBox::Ok);
    tcp->setObjectName(info);       //設置名稱,方便查找
    connect(tcp, SIGNAL(connected()), this, SLOT(onServerConnected()));
    connect(tcp, SIGNAL(disconnected()), this, SLOT(onServerDisconnected()));
    connect(tcp, SIGNAL(readyRead()), this, SLOT(onServerDataReady()));
    connect(tcp, SIGNAL(bytesWritten(qint64)), this, SLOT(onServerBytesWritten(qint64)));
}
void Widget::onServerConnected()
{

}

void Widget::onServerDisconnected()
{
    QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
    if( tcp != NULL )       //從鏈接對象中移除掉
    {
        qDebug() << "onServerDisconnected";
        qDebug() << "Local Address:" << tcp->peerAddress();
        qDebug() << "Local Port:" << tcp->peerPort();
        QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort());
        QMessageBox::information(this,"提示",QString("客戶端斷開鏈接:%1").arg(info),QMessageBox::Ok);

        int index = ui-> targetObject ->findText(info);
        if(index>=0)
        ui->targetObject->removeItem(index);
    }
}
void Widget::onServerDataReady()
{
    QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
    if(tcp->peerAddress().toString()!=targetAddr || tcp->peerPort()!=targetPort  )
    {
        targetAddr = tcp->peerAddress().toString();
        targetPort = tcp->peerPort();
        ui->recvEdit->insertPlainText("[接受來自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n");
    }
    ui->recvEdit->moveCursor(QTextCursor::End);
    ui->recvEdit->insertPlainText(QString::fromLocal8Bit(tcp->readAll())+"\r\n");
}
void Widget::onServerBytesWritten(qint64 bytes)
{
    qDebug() << "onBytesWritten:" << bytes;
    ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));
}
相關文章
相關標籤/搜索