天天看點

從零開始學Qt(87):TCP用戶端程式設計

作者:未來奇兵

本系列上一篇文章介紹了TCP伺服器端程式(TCPServer)設計的示例。本文繼續介紹TCP用戶端程式(TCPClient)設計的示例。

TCPClient程式具有如下的功能:

  • 通過IP位址和端口号連接配接到伺服器;
  • 采用基于行的資料通信協定,與伺服器端收發消息;
  • 處理QTcpSocket的StateChange()信号,在狀态欄顯示socket的狀态。
從零開始學Qt(87):TCP用戶端程式設計

1.主視窗定義與構造函數

用戶端程式TCPClient隻需要使用一個QTcpSocket對象,就可以和伺服器端程式TCPServer進行通信。

TCPClient也是一個視窗基于QMainWindow的應用程式,其主視窗的定義如下:

class MainWindow : public QMainWindow
{
	Q_OBJECT
public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();
protected:
	void closeEvent(QCloseEvent *event);
private:
  Ui::MainWindow *ui;
  QTcpSocket *tcpClient;//TCP 通信的 Socket
  QLabel *LabSocketState;//狀态欄标簽
  QString getLocalIP();//擷取本機 IP 位址
private slots:
  //自定義槽函數
  void onConnected(); //Socket connected
  void onDisconnected();//Socket disconnected
  void onSocketStateChange(QAbstractSocket::SocketState socketState);
  void onSocketReadyRead();//讀取 socket 傳入的資料
};           

這裡隻定義了一個用于socket連接配接和通信的QTcpSocket變量tcpClient,自定義了幾個槽函數, 用于與tcpClient的相關信号關聯。

下面是MainWindow的構造函數,主要功能是建立tcpClient,并建立信号與槽函數的關聯。

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  tcpClient=new QTcpSocket(this); //建立 socket 變量
  LabSocketState=new QLabel ("Socket 狀态:");//狀态欄标簽
  LabSocketState->setMinimumWidth(250);
  ui->statusbar->addWidget(LabSocketState);
  QString localIP=getLocalIP(); //本機 IP
  this->setWindowTitle(this->windowTitle() +" 本機 IP: "+localIP);
  ui->comboServer->addItem(localIP);
  connect(tcpClient,SIGNAL(connected()),this,SLOT(onConnected()));
  connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
  connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
  		this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
  connect(tcpClient,SIGNAL(readyRead()), this,SLOT(onSocketReadyRead()));
}           

2.與伺服器端建立socket連接配接

在視窗上設定伺服器IP位址和端口後,調用QTcpSocket的函數connectToHost()連接配接到伺服器,也可以使用disconnectFromHost()函數斷開與伺服器的連接配接。

下面是兩個按鈕的響應代碼,以及兩個相關槽函數的代碼:

void MainWindow::on_actionConnect_triggered()
{ // “連接配接到伺服器”按扭
  QString addr=ui->comboServer->currentText();
  quint16 port=ui->spinPort->value();
  tcpClient->connectToHost(addr,port);
}           
void MainWindow::on_actionDisconnect_triggered()
{// “斷開連接配接”按鈕
  if (tcpClient->state()==QAbstractSocket::ConnectedState)
  tcpClient->disconnectFromHost();
}           

槽函數onSocketStateChange()的功能和代碼與TCPServer中的完全一樣,這裡不再贅述。

3.與TCPServer的資料收發

TCPClient與TCPServer之間采用基于行的資料通信協定。單擊“發送消息”按鈕将發送一行 字元串。在readyRead()信号的槽函數裡讀取行字元串,其相關代碼如下:

void MainWindow::on_btnSend_clicked()
{ //發送一行字元串,以換行符結束
  QString msg=ui->edtMsg->text();
  ui->plainTextEdit->appendPlainText("[out] "+msg);
  ui->edtMsg->clear();
  ui->edtMsg->setFocus();
  QByteArray str=msg.toUtf8();
  str.append('\n');//添加一個換行符
  tcpClient->write(str);
}

void MainWindow::onSocketReadyRead()
{ //讀取緩沖區行文本
  while(tcpClient->canReadLine())
  ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
}           

執行個體TCPServer和TCPClient隻是簡單示範了 TCP通信的基本原理,TCPServer隻允許一個 TCPClient用戶端接入。而一般的TCP伺服器程式允許多個用戶端接入,為了使每個socket連接配接獨立通信互不影響,一般采用多線程,即為一個socket連接配接建立一個線程。

執行個體TCPServer和TCPClient之間的資料通信采用基于行的通信協定,隻能傳輸字元串資料。 QTcpSocket間接繼承于QIODevice,可以使用資料流的方式傳輸二進制資料流,例如傳輸圖檔、 任意格式檔案等,但是這涉及到伺服器端和用戶端之間通信協定的定義,本文不具體介紹了。

————————————————

覺得有用的話請關注點贊,謝謝您的支援!

對于本系列文章相關示例完整代碼有需要的朋友,可關注并在評論區留言!