QT -- TcpSocket實例,使用Qt中的tcp通信協議,構建客戶端和服務端,實現局域網通信軟件功能

1.簡介

TCP通信協議是面向對象,可靠的連接服務,正因爲這是它最大的特點,因此在諸多領域應用廣泛,在使用Qt進行面向對象的設計的時候,也常常用到TCP通信,而在Qt中,TCP協議被封裝成了一個更易調用的類。此篇博文將使用Qt中的TCP通信來實現客戶端和服務端的連接交互通信,以此作爲延伸。
在之前的一篇博文中 ,Linux中C語言構建TCP通信,使用了C語言對TCP協議進行調用,來實現客戶端和服務端的通信連接。而Qt中的TCP通信連接步驟和C語言中原生tcp連接的步驟一樣,大體都是分爲以下大類

  • 創建套接字
  • 綁定本機地址和端口
  • 設置監聽套接字
  • 主動向服務器(客戶端)發送請求
  • 客戶端(服務器)接受連接請求
  • 接受/發送數據
  • (雙方)關閉套接字

區別在於,Qt中調用TCP的方式簡單,可以實現如下的效果
在這裏插入圖片描述
以下是詳細的實際實現

2.項目創建和界面構建

1)流程圖

TCP協議通信邏輯圖有下圖,根據此邏輯圖,可以更好的理解Qt中的TCP協議的調用
在這裏插入圖片描述

2)項目構建

創建widget項目,除自有界面外,再添加一個新的界面,構建如下圖所示的項目文件樹

在這裏插入圖片描述

3)界面構建

客戶端界面構建

在這裏插入圖片描述
服務端界面構建

在這裏插入圖片描述

兩個界面大體是構建上面是顯示消息欄。下面是輸入消息發送欄。

3.代碼設計

1)項目pro添加

第一步需要在項目的pro文件中添加網絡通信模塊

QT       += network

2)客戶端設計

a. clientwidget.h

首先需要添加所需頭文件,用於tcp通信

#include <QTcpSocket>

之後是tcp通信的類的聲明以及一個相應鍵盤事件的事件過濾器

private:
    QTcpSocket *tcpSocket;
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件過濾器
};

b.clientwdige.cpp

同樣是添加所需工具頭文件

#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>

之後是構造函數和部分相關函數的設計

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);
    this->setWindowTitle("客戶端");
    this->resize(500,300);
    this->setMinimumSize(500,300);
    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket,&QTcpSocket::connected,
            [=]()
            {
                ui->textEditRead->setText("與服務器成功連接");
                qDebug() << "連接成功";

            }
            );
    connect(tcpSocket,&QTcpSocket::readyRead,
            [=]()
            {
                QByteArray array = tcpSocket->readAll();
                ui->textEditRead->append(array);
            }
            );
    ui->ButtonSend->setFocus();
    ui->ButtonSend->setDefault(true);
    //ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
    ui->textEditWrite->installEventFilter(this);//設置完後自動調用其eventFilter函數
}

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

void ClientWidget::on_ButtonConnect_clicked()
{
    //獲取服務器端口和ip
    QString ip = ui->lineEditIp->text();
    qint16 port = ui->lineEditPort->text().toInt();
    //與服務器進行連接
    tcpSocket->connectToHost(QHostAddress(ip),port);
}

void ClientWidget::on_ButtonSend_clicked()
{
    //獲取編輯框內容
    QTime cur_time = QTime::currentTime();
    QString str = ui->textEditWrite->toPlainText();
    QString time_info = cur_time.toString("hh:mm:ss");
    QString str_info = QString("客戶端: %1 [%2]").arg(str).arg(time_info);
    //發送數據
    tcpSocket->write(str_info.toUtf8().data());
    ui->textEditWrite->clear();

    //ui->textEditWrite->setFocus();
}

void ClientWidget::on_ButtonClose_clicked()
{
    //主動斷開連接
    tcpSocket->disconnectFromHost();
    ui->textEditRead->setText("與服務器斷開連接!");
    tcpSocket->close();
}

3)服務端設計

a.serverwidget.h

添加頭文件

#include <QTcpServer>   //監聽套接字
#include <QTcpSocket>   //通信套接字

通信套接字聲明實現和事件過濾器

private:
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件過濾器

b.serverwidget.cpp

添加工具頭文件

#include <QTime>
#include <QKeyEvent>

構造函數設計

ServerWidget::ServerWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ServerWidget)
{
    ui->setupUi(this);
    tcpServer = NULL;
    tcpSocket = NULL;
    //
    tcpServer  = new QTcpServer(this);
    tcpSocket = new QTcpSocket(this);
    tcpServer->listen(QHostAddress::Any,8888);
    this->setWindowTitle("服務器(端口:8888)");
    this->resize(500,300);
    this->setMinimumSize(500,300);

    connect(tcpServer,&QTcpServer::newConnection,
            [=]()
            {
                //取出建立好連接的套接字
                tcpSocket = tcpServer->nextPendingConnection();
                //獲取對方的ip和端口s
                static QString ip = tcpSocket->peerAddress().toString();
                static qint16 port = tcpSocket->peerPort();
                QString temp = QString("[%1:%2] : 成功連接").arg(ip).arg(port);
                ui->textEditRead->setText(temp);

                connect(tcpSocket,&QTcpSocket::readyRead,
                        [=]()
                        {
                            //從通信套接字中取出內容
                            QByteArray array = tcpSocket->readAll();
                            ui->textEditRead->append(array);        //追加添加內容
                        }
                        );
            }
            );
//    connect(tcpSocket,&QTcpSocket::readyRead,
//            [=]()
//            {
//                //從通信套接字中取出內容
//                QByteArray array = tcpSocket->readAll();
//                ui->textEditRead->append(array);        //追加添加內容
//            }
//            );
    ui->ButtonSend->setFocus();
    ui->ButtonSend->setDefault(true);
    //ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
    ui->textEditWrite->installEventFilter(this);//設置完後自動調用其eventFilter函數

}

經此設計構建之後可以實現如上的功能操作,有此基礎之後可以自定定義自己的服務器,如智能小車的控制,攝像頭圖像的傳遞識別等等

4.源代碼附錄

clientwidget.h

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include <QWidget>
#include <QTcpSocket>

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ClientWidget(QWidget *parent = nullptr);
    ~ClientWidget();

private slots:
    void on_ButtonSend_clicked();

    void on_ButtonConnect_clicked();

    void on_ButtonClose_clicked();

private:
    Ui::ClientWidget *ui;

private:
    QTcpSocket *tcpSocket;
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件過濾器
};

#endif // CLIENTWIDGET_H

clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);
    this->setWindowTitle("客戶端");
    this->resize(500,300);
    this->setMinimumSize(500,300);
    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket,&QTcpSocket::connected,
            [=]()
            {
                ui->textEditRead->setText("與服務器成功連接");
                qDebug() << "連接成功";

            }
            );
    connect(tcpSocket,&QTcpSocket::readyRead,
            [=]()
            {
                QByteArray array = tcpSocket->readAll();
                ui->textEditRead->append(array);
            }
            );
    ui->ButtonSend->setFocus();
    ui->ButtonSend->setDefault(true);
    //ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
    ui->textEditWrite->installEventFilter(this);//設置完後自動調用其eventFilter函數
}

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

void ClientWidget::on_ButtonConnect_clicked()
{
    //獲取服務器端口和ip
    QString ip = ui->lineEditIp->text();
    qint16 port = ui->lineEditPort->text().toInt();
    //與服務器進行連接
    tcpSocket->connectToHost(QHostAddress(ip),port);
}

void ClientWidget::on_ButtonSend_clicked()
{
    //獲取編輯框內容
    QTime cur_time = QTime::currentTime();
    QString str = ui->textEditWrite->toPlainText();
    QString time_info = cur_time.toString("hh:mm:ss");
    QString str_info = QString("客戶端: %1 [%2]").arg(str).arg(time_info);
    //發送數據
    tcpSocket->write(str_info.toUtf8().data());
    ui->textEditWrite->clear();

    //ui->textEditWrite->setFocus();
}



void ClientWidget::on_ButtonClose_clicked()
{
    //主動斷開連接
    tcpSocket->disconnectFromHost();
    ui->textEditRead->setText("與服務器斷開連接!");
    tcpSocket->close();
}

bool ClientWidget::eventFilter(QObject *target, QEvent *event)
{
    if(target == ui->textEditWrite)
        {
            if(event->type() == QEvent::KeyPress)//回車鍵
            {
                 QKeyEvent *k = static_cast<QKeyEvent *>(event);

                 if(k->key() == Qt::Key_Return)
                 {
                     on_ButtonSend_clicked();
                     return true;
                 }
            }
        }
        return QWidget::eventFilter(target,event);

}

serverwidget.h

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include <QTcpServer>   //監聽套接字
#include <QTcpSocket>   //通信套接字

namespace Ui {
class ServerWidget;
}

class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ServerWidget(QWidget *parent = nullptr);
    ~ServerWidget();
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件過濾器

private slots:
    void on_ButtonClose_clicked();

    void on_ButtonSend_clicked();

private:
    Ui::ServerWidget *ui;

private:
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;

//    QString ip;
//    qint16 port;

};

#endif // SERVERWIDGET_H

serverwidget.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"
#include <QTime>
#include <QKeyEvent>

ServerWidget::ServerWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ServerWidget)
{
    ui->setupUi(this);
    tcpServer = NULL;
    tcpSocket = NULL;
    //
    tcpServer  = new QTcpServer(this);
    tcpSocket = new QTcpSocket(this);
    tcpServer->listen(QHostAddress::Any,8888);
    this->setWindowTitle("服務器(端口:8888)");
    this->resize(500,300);
    this->setMinimumSize(500,300);

    connect(tcpServer,&QTcpServer::newConnection,
            [=]()
            {
                //取出建立好連接的套接字
                tcpSocket = tcpServer->nextPendingConnection();
                //獲取對方的ip和端口s
                static QString ip = tcpSocket->peerAddress().toString();
                static qint16 port = tcpSocket->peerPort();
                QString temp = QString("[%1:%2] : 成功連接").arg(ip).arg(port);
                ui->textEditRead->setText(temp);

                connect(tcpSocket,&QTcpSocket::readyRead,
                        [=]()
                        {
                            //從通信套接字中取出內容
                            QByteArray array = tcpSocket->readAll();
                            ui->textEditRead->append(array);        //追加添加內容
                        }
                        );
            }
            );
//    connect(tcpSocket,&QTcpSocket::readyRead,
//            [=]()
//            {
//                //從通信套接字中取出內容
//                QByteArray array = tcpSocket->readAll();
//                ui->textEditRead->append(array);        //追加添加內容
//            }
//            );
    ui->ButtonSend->setFocus();
    ui->ButtonSend->setDefault(true);
    //ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
    ui->textEditWrite->installEventFilter(this);//設置完後自動調用其eventFilter函數

}

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

bool ServerWidget::eventFilter(QObject *target, QEvent *event)
{
    if(target == ui->textEditWrite)
        {
            if(event->type() == QEvent::KeyPress)//回車鍵
            {
                 QKeyEvent *k = static_cast<QKeyEvent *>(event);

                 if(k->key() == Qt::Key_Return)
                 {
                     on_ButtonSend_clicked();
                     return true;
                 }
            }
        }
        return QWidget::eventFilter(target,event);

}


void ServerWidget::on_ButtonSend_clicked()
{
    if(tcpSocket == NULL)
    {
        return ;
    }
    //獲取編輯區內容
    QTime cur_time = QTime::currentTime();
    QString str = ui->textEditWrite->toPlainText();
    QString time_info = cur_time.toString("hh:mm:ss");
    QString str_info = QString("服務器: %1 [%2]").arg(str).arg(time_info);
    tcpSocket->write(str_info.toUtf8().data());
    ui->textEditWrite->clear();
}

void ServerWidget::on_ButtonClose_clicked()
{
    if(tcpSocket == NULL)
    //主動與客戶端斷開連接
    tcpSocket->disconnectFromHost();

    static QString ip = tcpSocket->peerAddress().toString();
    static qint16 port = tcpSocket->peerPort();
    QString showtemp = QString("[%1:%2] : 斷開連接").arg(ip).arg(port);
    ui->textEditRead->setText(showtemp);

    tcpSocket->close();
    //this->close();
    tcpSocket = NULL;

}

詳細效果,可戳傳送門下載體驗傳送門

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章