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

  • 2019 年 10 月 3 日
  • 筆記

8.9更新說明

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

 


 

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

PS:支持客戶端和服務器,提供源碼,並且服務器支持多客戶端連入,並且可以指定與個別客戶端發送數據,也可以給所有連入的客戶端發送數據.

1.效果圖所下所示:

 

如下圖所示,當服務器狀態下,如果有客戶端連入,會提示客戶端信息:

 

2.效果操作

客戶端操作:

 

服務器操作:

從上面操作可以看出,服務器支持多客戶端連入,並且可以指定與個別客戶端發送數據,也可以給所有連入的客戶端發送數據.

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)+"]:rn");      }      ui->recvEdit->moveCursor(QTextCursor::End);      ui->recvEdit->insertPlainText(QString::fromLocal8Bit(m_client.readAll())+"rn");  }  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)+"]:rn");      }      ui->recvEdit->moveCursor(QTextCursor::End);      ui->recvEdit->insertPlainText(QString::fromLocal8Bit(tcp->readAll())+"rn");  }  void Widget::onServerBytesWritten(qint64 bytes)  {      qDebug() << "onBytesWritten:" << bytes;      ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));  }