QT tcp与udp网络通信以及定时器的使用 (7)

QT tcp与udp网络通信以及定时器的使用


文章目录

  • QT tcp与udp网络通信以及定时器的使用
  • 1、QT网络与通信简单介绍
  • 2、QT TCP通信
    • 1、 服务器的流程
    • 2、 客户端的流程
    • 3、服务器的编写
    • 4、客户端的编写
  • 3、QT UDP通信
    • 1、客户端流程
    • 2、客户端编写
    • 3、UDP广播
    • 4、UDP组播
  • 4、定时器的用法
    • 1、方法一
    • 2、方法2
    • 2、方法3(不建议使用)
  • 5、Tcp传文件
    • 1、服务器编写
    • 2、客户端编写
  • 6、tcp与udp对比
  • 7.总结


1、QT网络与通信简单介绍

QT5提供了一套完善的网络模块,包括了TCP、UDP、HTTP等协议的支持,可以方便地在QT应用程序中进行网络通信。通过QT5的网络模块,开发者可以实现客户端和服务器之间的数据传输、消息推送、远程控制等功能。
Qt中提供的所有的Socket类都是非阻塞的。
Qt中常用的用于socket通信的套接字类:
QTcpServer
用于TCP/IP通信, 作为服务器端套接字使用
QTcpSocket
用于TCP/IP通信,作为客户端套接字使用。
QUdpSocket
用于UDP通信,服务器,客户端均使用此套接字。
QSslSocket
用于实现安全的网络通信、
QWebSocket
用于实现WebSocket协议的通信等。
下面是继承关系:
QT tcp与udp网络通信以及定时器的使用 (7)_第1张图片

2、QT TCP通信

TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为:TCP)是一种面向连接的、可靠的、基于字节流的通信协议。分为服务器与客户端。

1、 服务器的流程

QT tcp与udp网络通信以及定时器的使用 (7)_第2张图片

2、 客户端的流程

QT tcp与udp网络通信以及定时器的使用 (7)_第3张图片
总体双方的流程如下:
QT tcp与udp网络通信以及定时器的使用 (7)_第4张图片

3、服务器的编写

完成的结果如下:客户端点击连接服务器按钮。成功连接就可以相互进行通信了。
QT tcp与udp网络通信以及定时器的使用 (7)_第5张图片

注意,要使用QT的网络模块,需要在.pro文件中添加QT += network声明,以添加网络模块的依赖。

  1. 声明TCP监听套接字与通信套接字
#include 
#include 
QTcpServer *pTcpListenSocket = nullptr;
QTcpSocket *pTcpCommunicatSocket = nullptr;
  1. 创建QTcpServer套接字。QTcpSocket套接字在客户端成功连接时获取。此时为空。
pTcpListenSocket = new QTcpServer(this);
pTcpCommunicatSocket = nullptr;
  1. 监听客户端连接请求并处理
pTcpListenSocket->listen(QHostAddress::Any, 7777); /* 服务器地址和端口 */
  1. 获取通信套接字
/* 取出建立好的连接套接字 */
 pTcpCommunicatSocket = pTcpListenSocket->nextPendingConnection();
  1. 最后就可以进行发送接收数据
/* 发送给客户端数据接口 */
pTcpCommunicatSocket->write(sendData);
/* 接收客户端的数据接口 */
QByteArray arry = pTcpCommunicatSocket->readAll();
  1. 断开连接
pTcpClientSocket->disconnectFromHost();
pTcpClientSocket->close();

服务器整体的代码如下:
首先Ui的布局:
QT tcp与udp网络通信以及定时器的使用 (7)_第6张图片
serveridget.h

#include 
#include 
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class Serveridget; }
QT_END_NAMESPACE

class Serveridget : public QWidget
{
    Q_OBJECT

public:
    Serveridget(QWidget *parent = nullptr);
    ~Serveridget();
    QString ip;
    quint16 port;
private:
    QTcpServer *pTcpListenSocket = nullptr;
    QTcpSocket *pTcpCommunicatSocket = nullptr;
private slots:
    void ConnecSucceSlot(void);
    void ReceDealSlot(void);
    void SendDataSlot(void);
    void DisServerSlot(void);
    void ClearReceSlot(void);
    void ClearSendSlot(void);
private:
    Ui::Serveridget *ui;
};

serveridget.cpp

#include "serveridget.h"
#include "ui_serveridget.h"
#include 
#include 

Serveridget::Serveridget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Serveridget)
{
    ui->setupUi(this);
    pTcpListenSocket = new QTcpServer(this);
    this->setWindowTitle("服务器端口:7777");
    this->resize(400,400);
    ui->textEditRead->setPlaceholderText("接收区");
    pTcpListenSocket->listen(QHostAddress::Any, 7777); /* 服务器地址和端口 */

    /* 客户端与服务器连接成功监听套接字会触发newConnection */
    connect(pTcpListenSocket, &QTcpServer::newConnection, this, &Serveridget::ConnecSucceSlot);

    /* 服务器发送数据 */
    connect(ui->pushButtonSendData, &QPushButton::pressed, this, &Serveridget::SendDataSlot);

    /* 断开与客户端的连接 */
    connect(ui->pushButtonClose, &QPushButton::pressed, this, &Serveridget::DisServerSlot);

    /* 清空接收区 */
    connect(ui->pushButtonClearSend, &QPushButton::pressed, this, &Serveridget::ClearSendSlot);

    /* 清空发送区 */
    connect(ui->pushButtonClearReceive, &QPushButton::pressed, this, &Serveridget::ClearReceSlot);

}

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

void Serveridget::ConnecSucceSlot(void)
{
    /* 取出建立好的连接套接字 */
    pTcpCommunicatSocket = pTcpListenSocket->nextPendingConnection();

    /* 获取连接成功客户端的ip与端口 */
    ip = pTcpCommunicatSocket->peerAddress().toString();
    port = pTcpCommunicatSocket->peerPort();
    QString str = QString("[%1]:[%2]:[%3]:连接成功").arg(ip).arg(port); /* 组包 */
    ui->textEditRead->setText(str); /* 显示连接成功 */
    /* 当客户端发送数据服务器成功接收通信套接字触发readyRead信号 */
    connect(pTcpCommunicatSocket, &QTcpSocket::readyRead, this, &Serveridget::ReceDealSlot);
}

void Serveridget::ReceDealSlot(void)
{
    if (pTcpCommunicatSocket == nullptr) {
        return;
    }
    /* 接收客户端的数据 */
    QByteArray arry = pTcpCommunicatSocket->readAll();
    /* 显示数据 */
    ui->textEditRead->append(arry);
}

void Serveridget::SendDataSlot(void)
{
    if (pTcpCommunicatSocket == nullptr) {
        return;
    }
    /* 获取发送的数据 */
    QByteArray sendData = ui->textEditWrite->toPlainText().toUtf8().data();
    /* 发送的数据  */
    pTcpCommunicatSocket->write(sendData);
}

void Serveridget::DisServerSlot(void)
{
    if (pTcpCommunicatSocket == nullptr) {
        return;
    }
    /* 主动和客户端端口连接 */
    pTcpCommunicatSocket->disconnectFromHost();
    pTcpCommunicatSocket->close();
    pTcpCommunicatSocket = nullptr;
    QString str = QString("[%1]:[%2]:断开连接").arg(ip).arg(port); /* 组包 */
    ui->textEditRead->setText(str); /* 显示连接成功 */
}

void Serveridget::ClearReceSlot(void)
{
    ui->textEditRead->clear();
}

void Serveridget::ClearSendSlot(void)
{
    ui->textEditWrite->clear();
}

4、客户端的编写

  1. 声明TCP通信套接字
#include 
QTcpSocket *pTcpClientSocket = nullptr;
  1. 创建TCP通信套接字
pTcpClientSocket = new QTcpSocket(this);
  1. 主动与服务器建立连接
/* 主动与服务器建立连接 */
pTcpClientSocket->connectToHost(QHostAddress(ip), port);
  1. 读写数据
/* 发送数据给服务器 */
pTcpClientSocket->write(sendData);
/* 接收服务器的数据 */
QByteArray arry =  pTcpClientSocket->readAll();
  1. 断开连接
pTcpClientSocket->disconnectFromHost();
pTcpClientSocket->close();

客户端整体的代码如下:
首先Ui的布局:
QT tcp与udp网络通信以及定时器的使用 (7)_第7张图片
客户端的创建:
QT tcp与udp网络通信以及定时器的使用 (7)_第8张图片
QT tcp与udp网络通信以及定时器的使用 (7)_第9张图片

clientwidget.h

#include 
#include 
namespace Ui {
class Clientwidget;
}
class Clientwidget : public QWidget
{
    Q_OBJECT

public:
    explicit Clientwidget(QWidget *parent = nullptr);
    ~Clientwidget();
    QTcpSocket *pTcpClientSocket = nullptr;
private slots:
    void ConnectServerSlot(void);
    void ConnectSucceSlot(void);
    void SendDataSlot(void);
    void ReadDataSlot(void);
    void DisconnectSlot(void);
    void DisconnecServerSlot(void);
    void ClearReceSlot(void);
    void ClearSendSlot(void);
private:
    Ui::Clientwidget *ui;
};

clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include 
#include 

Clientwidget::Clientwidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Clientwidget)
{
    ui->setupUi(this);
    pTcpClientSocket = new QTcpSocket(this);
    this->setWindowTitle("客户端");
    this->resize(400,400);
    ui->textEditRead->setPlaceholderText("接收区");
    /* 主动与服务器连接 */
    connect(ui->pushButtonConnect, &QPushButton::pressed, this, &Clientwidget::ConnectServerSlot);

    /* 如果成功与服务器建立连接通信套接字触发connected */
    connect(pTcpClientSocket, &QTcpSocket::connected, this, &Clientwidget::ConnectSucceSlot);

    /* 如果成功与服务器断开连接通信套接字触发disconnected */
    connect(pTcpClientSocket, &QTcpSocket::disconnected, this, &Clientwidget::DisconnectSlot);

    /* 接收服务器的数据 */
    connect(pTcpClientSocket, &QTcpSocket::readyRead, this, &Clientwidget::ReadDataSlot);

    /* 发送数据 */
    connect(ui->pushButtonsendData, &QPushButton::pressed, this, &Clientwidget::SendDataSlot);

    /* 断开与客户端的连接 */
    connect(ui->pushButtonDisconnec, &QPushButton::pressed,this, &Clientwidget::DisconnecServerSlot);


    /* 清空接收区 */
    connect(ui->pushButtonClearSend, &QPushButton::pressed, this, &Clientwidget::ClearSendSlot);

    /* 清空发送区 */
    connect(ui->pushButtonClearRece, &QPushButton::pressed, this, &Clientwidget::ClearReceSlot);
}

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

void Clientwidget::ConnectServerSlot(void)
{
    /* 获取服务器ip和端口 */
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();
    /* 主动与服务器建立连接 */
    pTcpClientSocket->connectToHost(QHostAddress(ip), port);
}
void Clientwidget::ConnectSucceSlot(void)
{
    ui->textEditRead->append("成功与客户端连接");
}
void Clientwidget::SendDataSlot(void)
{
    /* 获取发送的数据 */
    QByteArray sendData = ui->textEditWrite->toPlainText().toUtf8().data();
    pTcpClientSocket->write(sendData);
}
void Clientwidget::ReadDataSlot(void)
{
    QByteArray arry =  pTcpClientSocket->readAll();
    ui->textEditRead->append(arry);
}
void Clientwidget::DisconnectSlot(void)
{
    ui->textEditRead->append("断开与客户端连接");
}
void Clientwidget::DisconnecServerSlot(void)
{
    if (pTcpClientSocket == nullptr) {
        return;
    }
    pTcpClientSocket->disconnectFromHost();
    pTcpClientSocket->close();
}
void Clientwidget::ClearReceSlot(void)
{
    ui->textEditRead->clear();
}
void Clientwidget::ClearSendSlot(void)
{
    ui->textEditWrite->clear();
}

main.cpp

#include "serveridget.h"
#include "clientwidget.h"
#include 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Serveridget serverWid;
    Clientwidget clientWid;
    serverWid.show();
    clientWid.show();
    return a.exec();
}

3、QT UDP通信

使用Qt提供的QUdpSocket进行UDP通信。在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发送数据。类似的服务器也不从客户端接收连接,只负责调用接收函数,等待来自客户端的数据的到达。
在UDP通信中,服务器端和客户端的概念已经显得有些淡化,两部分做的工作都大致相同.

1、客户端流程

QT tcp与udp网络通信以及定时器的使用 (7)_第10张图片
创建其它的客户端也是一样的流程。
总体双方的流程如下:
QT tcp与udp网络通信以及定时器的使用 (7)_第11张图片

2、客户端编写

注意,要使用QT的网络模块,需要在.pro文件中添加QT += network声明,以添加网络模块的依赖。
最后的效果如下:
QT tcp与udp网络通信以及定时器的使用 (7)_第12张图片

客户端的ui界面:
QT tcp与udp网络通信以及定时器的使用 (7)_第13张图片

  1. 声明UDP通信套接字
#include 
QUdpSocket *pUdpSocket = nullptr;
  1. 创建UDP通信套接字
pUdpSocket = new QUdpSocket(this);
  1. 绑定端口
pUdpSocket->bind(QHostAddress::Any,7777);
  1. 读写数据
/* 获取数据 */
 pUdpSocket->writeDatagram(sendData, QHostAddress(strIP), port);
/* 读取对方发送的内容 */
char buf[1024] = {0}; /* 保存对方的数据 */
QHostAddress cliAddr; /* 对方地址 */
quint16 port;    /* 对方端口 */
qint64 size = pUdpSocket->readDatagram(buf, sizeof(buf), &cliAddr, &port);

总体的代码如下:
clientwidget.h

#include 
#include 
QT_BEGIN_NAMESPACE
namespace Ui { class ClientWidget; }
QT_END_NAMESPACE
class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    ClientWidget(QWidget *parent = nullptr);
    ~ClientWidget();
    QUdpSocket *pUdpSocket = nullptr;
private slots:
    void ReadDataSlot(void);
    void WriteDataSlot(void);
private:
    Ui::ClientWidget *ui;
};

clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include 
#include 

ClientWidget::ClientWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ClientWidget)
{
    ui->setupUi(this);
    pUdpSocket = new QUdpSocket(this);
    /* 绑定端口 */
    pUdpSocket->bind(QHostAddress::Any,7777);
    this->setWindowTitle("服务器端口:7777");
    this->resize(400,400);
    ui->textEditRece->setPlaceholderText("接收区");


    /* 发送数据 */
    connect(ui->pushButtonSend, &QPushButton::pressed, this, &ClientWidget::WriteDataSlot);
    /* 接收到readyRead信号 */
    connect(pUdpSocket, &QUdpSocket::readyRead, this, &ClientWidget::ReadDataSlot);
}

ClientWidget::~ClientWidget()
{
    delete ui;
}
void ClientWidget::WriteDataSlot(void)
{
    /* 获取发送对方的端口与IP */
    QString strIP = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();
    QByteArray sendData =  ui->textEditSend->toPlainText().toUtf8().data();

    if (strIP.isEmpty() || sendData.size() == 0) {
        qDebug() << "isEmpty";
        return;
    }
    /* 获取数据 */
    pUdpSocket->writeDatagram(sendData, QHostAddress(strIP), port);
}

void ClientWidget::ReadDataSlot(void)
{
    /* 读取对方发送的内容 */
    char buf[1024] = {0}; /* 保存对方的数据 */
    QHostAddress cliAddr; /* 对方地址 */
    quint16 port;    /* 对方端口 */
    qint64 size = pUdpSocket->readDatagram(buf, sizeof(buf), &cliAddr, &port);
    qDebug() << "size:"<<size;
    if (size > 0) {
        /* 格式化 [192.68.2.2:8888]aaaa */
        QString str = QString("[%1:%2] %3")
                          .arg(cliAddr.toString())
                          .arg(port)
                          .arg(buf);
        ui->textEditRece->append(str);
    }
}

例外一端是一样的。把绑定端口换一下就行。

pUdpSocket->bind(QHostAddress::Any,8888);

3、UDP广播

QT tcp与udp网络通信以及定时器的使用 (7)_第14张图片

4、UDP组播

QT tcp与udp网络通信以及定时器的使用 (7)_第15张图片
注意组播在绑定是IP要选择QHostAddress::AnyIPv4 并且组播是D类地址。
udpSocket->leaveMulticastGroup(QHostAddress(“224.0.0.2”)) 如果不想接收也可以退出这个组播。

4、定时器的用法

1、方法一

最后的效果:
QT tcp与udp网络通信以及定时器的使用 (7)_第16张图片
只是简单使用两个不同的定时器。
1、定义一个QTimer对象

QTimer* timer;
timer = new QTimer(this);
  1. 启动定时器
timer->start(1000)

3 . 连接信号槽
当start启动定时器就会每隔设定的时间触发timeout信号

connect(timer, &QTimer::timeout,
            [=]()
            {
            /* 定时器时间到的处理 */
            }
            );
  1. 停止计时
timer->stop();

mywidget.h

#include 
#include 
QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
    Q_OBJECT
public:
    MyWidget(QWidget *parent = nullptr);
    ~MyWidget();
    QTimer *pTime_1;
    QTimer *pTime_2;
    int i = 0;
    int j = 0;

private slots:
    void on_pushButtonStartOne_clicked();

    void on_pushButtonStartTwo_clicked();

    void on_pushButtonStopOne_clicked();

    void on_pushButtonStoptwo_clicked();

private:
    Ui::MyWidget *ui;
};

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);
    pTime_1 = new QTimer(this);
    pTime_2 = new QTimer(this);
    i = 0;
    j = 0;
    connect(pTime_1, &QTimer::timeout,
            [=]()
            {
                i++;
                ui->lcdNumberOne->display(i);
            }

            );
    connect(pTime_2, &QTimer::timeout,
            [=]()
            {
                j++;
                ui->lcdNumberTwo->display(j);
            }

            );
}

MyWidget::~MyWidget()
{
    delete ui;
}
void MyWidget::on_pushButtonStartOne_clicked()
{
    //启动定时器
    //时间间隔为1s
    //每隔1s,定时器pTime_1自动触发timeout()
    //如果定时器没有激活,才启动
    if(pTime_1->isActive() == false)
    {
        pTime_1->start(1000);
    }
}

void MyWidget::on_pushButtonStartTwo_clicked()
{
    if(pTime_2->isActive() == false)
    {
        pTime_2->start(1000);
    }
}

void MyWidget::on_pushButtonStopOne_clicked()
{
    if(pTime_1->isActive() == true)
    {
        pTime_1->stop();
    }
}

void MyWidget::on_pushButtonStoptwo_clicked()
{
    if(pTime_2->isActive() == true)
    {
        pTime_2->stop();
    }
}

2、方法2

1、重写虚函数

void timerEvent(QTimerEvent* e);

2、启动定时器

/* 返回定时器的Id 并且是唯一的 就是区分不同定时器 */
timeId_1 = startTimer(1000); 
  1. 定时器时间到进入timerEvent事件
void MyWidget::timerEvent(QTimerEvent *e)
{
    static int i = 0;
    static int j = 0;
    if (e->timerId() == timeId_1) {
        ui->lcdNumberOne->display(++i);
    } else if (e->timerId()== timeId_2) {
        ui->lcdNumberTwo->display(++j);
    }
}
  1. 关闭定时器
killTimer(timeId_1);

mywidget.h

#include 
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget(QWidget *parent = nullptr);
    ~MyWidget();
    int timeId_1 = 0;
    int timeId_2 = 0;
protected:
    void timerEvent(QTimerEvent *e);
private slots:
    void on_pushButtonStartOne_clicked();

    void on_pushButtonStartTwo_clicked();

    void on_pushButtonStopOne_clicked();

    void on_pushButtonStoptwo_clicked();

private:
    Ui::MyWidget *ui;
};

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);

}

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

void MyWidget::timerEvent(QTimerEvent *e)
{
    static int i = 0;
    static int j = 0;
    if (e->timerId() == timeId_1) {
        ui->lcdNumberOne->display(++i);
    } else if (e->timerId()== timeId_2) {
        ui->lcdNumberTwo->display(++j);
    }
}

void MyWidget::on_pushButtonStartOne_clicked()
{
    //启动定时器
    //时间间隔为1000ms
    //每隔1s,进入timerEvent事件
    timeId_1 = startTimer(1000);
}


void MyWidget::on_pushButtonStartTwo_clicked()
{
    timeId_2 = startTimer(4000);
}


void MyWidget::on_pushButtonStopOne_clicked()
{
    killTimer(timeId_1);
}


void MyWidget::on_pushButtonStoptwo_clicked()
{
    killTimer(timeId_2);
}


2、方法3(不建议使用)

singleShot静态函数
原型:void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
解释:这个静态函数在一个给定时间间隔 msec(毫秒) 之后调用一个槽。
只调一次:
假设类A有个槽函数 function() { }
我们要在1s之后执行它

QTimer::singleShot(1000,this, &A::function())

实现循环:
槽函数中还是singleShot 即:
这样的话就是一个每1秒执行一次的定时器

bool condition = true;
function(){
    if(condition){  //条件控制
        QTimer::singleShot(1000,this, &A::function());
    }
}

5、Tcp传文件

具体的流程图如下:
QT tcp与udp网络通信以及定时器的使用 (7)_第17张图片最终的效果如下:
QT tcp与udp网络通信以及定时器的使用 (7)_第18张图片
注意,要使用QT的网络模块,需要在.pro文件中添加QT += network声明,以添加网络模块的依赖。

1、服务器编写

服务器ui的设计:
QT tcp与udp网络通信以及定时器的使用 (7)_第19张图片
serverwidget.h

#include 
#include 
#include 
#include 
#include 

namespace Ui {
class ServerWidget;
}

class ServerWidget : public QWidget
{
    Q_OBJECT

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

    void sendData(); //发送文件数据

private slots:
    void on_buttonFile_clicked();
    void on_buttonSend_clicked();
private:
    Ui::ServerWidget *ui;
    QTcpServer *tcpServer; //监听套接字
    QTcpSocket *tcpSocket; //通信套接字
    QFile file; //文件对象
    QString fileName; //文件名字
    qint64 fileSize; //文件大小
    qint64 sendSize; //已经发送文件的大小
    QTimer timer; //定时器

};

serverwidget.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"
#include 

#include 

ServerWidget::ServerWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    /* 创建监听套接字 */
    tcpServer = new QTcpServer(this);

    /* 监听 */
    tcpServer->listen(QHostAddress::Any, 8888);

    /* 设置窗口标题 */
    setWindowTitle("服务器端口为:8888");

    /* 失能两个按钮都不能按 连接成功才可以按 */
    ui->buttonFile->setEnabled(false);
    ui->buttonSend->setEnabled(false);

    //如果客户端成功和服务器连接
    //tcpServer会自动触发 newConnection()
    connect(tcpServer, &QTcpServer::newConnection,
    [=]()
    {
        //取出建立好连接的套接字
        tcpSocket = tcpServer->nextPendingConnection();
        //获取对方的ip和端口
        QString ip = tcpSocket->peerAddress().toString();
        quint16 port = tcpSocket->peerPort();

        QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
        ui->textEdit->setText(str); //显示到编辑区

        //成功连接后,才能按选择文件
        ui->buttonFile->setEnabled(true);

        connect(tcpSocket, &QTcpSocket::readyRead,
                [=]()
                {
                    //取客户端的信息
                    QByteArray buf = tcpSocket->readAll();

                    /* 接收到客户端"file done" 说明客户端接收完成 */
                    if(QString(buf) == "file done")
                    {   //文件接收完毕
                         ui->textEdit->append("文件发送完毕");
                         file.close();

                         //断开客户端端口
                         tcpSocket->disconnectFromHost();
                         tcpSocket->close();
                    }

                }

                );

    }
    );

    connect(&timer, &QTimer::timeout,
            [=]()
            {
                //关闭定时器
                timer.stop();

                //发送文件数据
                sendData();
            }

            );

}

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

//选择文件的按钮
void ServerWidget::on_buttonFile_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(this, "open", "../");
    if(false == filePath.isEmpty()) //如果选择文件路径有效
    {
        fileName.clear();
        fileSize = 0;

        QFileInfo info(filePath);//获取文件信息
        fileName = info.fileName(); //获取文件名字
        fileSize = info.size(); //获取文件大小

        sendSize = 0; //发送文件的大小

        //只读方式打开文件
        //指定文件的名字
        file.setFileName(filePath);

        //打开文件
        bool isOk = file.open(QIODevice::ReadOnly);
        if(false == isOk)
        {
            qDebug() << "只读方式打开文件失败 106";
        }

        //提示打开文件的路径
        ui->textEdit->append(filePath);

        ui->buttonFile->setEnabled(false);
        ui->buttonSend->setEnabled(true);

    }
    else
    {
        qDebug() << "选择文件路径出错 118";
    }

}
//发送文件按钮
void ServerWidget::on_buttonSend_clicked()
{
    ui->buttonSend->setEnabled(false);

    //先发送文件头信息  格式:文件名##文件大小
    QString head = QString("%1##%2").arg(fileName).arg(fileSize);
    //发送头部信息
    qint64 len = tcpSocket->write( head.toUtf8() );
    if(len > 0)//头部信息发送成功
    {
        //发送真正的文件信息
        //防止TCP黏包
        //需要通过定时器延时 20 ms 在发送文件数据
        timer.start(20);


    }
    else
    {
        qDebug() << "头部信息发送失败 142";
        file.close();
        ui->buttonFile->setEnabled(true);
        ui->buttonSend->setEnabled(false);
    }
}

void ServerWidget::sendData()
{
    ui->textEdit->append("正在发送文件……");
     qint64 len = 0;
     do
     {
        //每次发送数据的大小 4K
        char buf[4*1024] = {0};
        len = 0;

        //往文件中读数据
        len = file.read(buf, sizeof(buf));
        //发送数据,读多少,发多少
        len = tcpSocket->write(buf, len);

        //发送的数据需要累积
        sendSize += len;

     }while(len > 0);

}

2、客户端编写

客户端ui的设计:
QT tcp与udp网络通信以及定时器的使用 (7)_第20张图片
功能:当客户端与服务器成功连接后。服务器选择发送一个文件给客户端。
clientwidget.h

#include 
#include 
#include 

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ClientWidget(QWidget *parent = 0);
    ~ClientWidget();
private slots:
    void on_buttonConnect_clicked();
private:
    Ui::ClientWidget *ui;
    QTcpSocket *tcpSocket;
    QFile file; //文件对象
    QString fileName; //文件名字
    qint64 fileSize; //文件大小
    qint64 recvSize; //已经接收文件的大小

    bool isStart;   //标志位,是否为头部信息
};

clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
//#include 
#include 
#include 

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);

    tcpSocket = new QTcpSocket(this);

    isStart = true;

    ui->progressBar->setValue(0); //当前值

    setWindowTitle("客户端");

    connect(tcpSocket, &QTcpSocket::connected,
    [=]()
    {
        //提示连接成功
        ui->textEdit->clear();
        ui->textEdit->append("和服务器连接成功,等待服务器传送文件……");
    }
    );

    connect(tcpSocket, &QTcpSocket::readyRead,
    [=]()
    {
        //取出接收的内容
        QByteArray buf = tcpSocket->readAll();

        if(true == isStart)
        {//接收头
            isStart = false;
            //解析头部信息 QString buf = "hello##1024"
            //                    QString str = "hello##1024#mike";
            //                            str.section("##", 0, 0)

            //初始化
            //文件名
            fileName = QString(buf).section("##", 0, 0);
            //文件大小
            fileSize = QString(buf).section("##", 1, 1).toInt();
            recvSize = 0;   //已经接收文件大小

            //打开文件
            //关联文件名字
            file.setFileName(fileName);

            //只写方式方式,打开文件
            bool isOk = file.open(QIODevice::WriteOnly);
            if(false == isOk)
            {
                qDebug() << "WriteOnly error 49";

                tcpSocket->disconnectFromHost(); //断开连接
                tcpSocket->close(); //关闭套接字

                return; //如果打开文件失败退出。
            }

            //弹出对话框,显示接收文件的信息
            QString str = QString("接收的文件: [%1: %2kb]").arg(fileName).arg(fileSize/1024);
            //QMessageBox::information(this, "文件信息", str);
            ui->textEdit->append(str);
            ui->textEdit->append("正在接收文件……");

            //设置进度条
            ui->progressBar->setMinimum(0); //最小值
            ui->progressBar->setMaximum(fileSize/1024); //最大值
            ui->progressBar->setValue(0); //当前值

        }
        else //文件信息
        {
            qint64 len = file.write(buf);
            if(len >0) //接收数据大于0
            {
                recvSize += len; //累计接收大小
                qDebug() << len;
            }

            //更新进度条
            ui->progressBar->setValue(recvSize/1024); // 1024:防止文件太大越界 / 1024

            if(recvSize == fileSize) //文件接收完毕
            {

                //先给服务发送(接收文件完成的信息)
                tcpSocket->write("file done");

                ui->textEdit->append("文件接收完成");
                QMessageBox::information(this, "完成", "文件接收完成");
                file.close(); //关闭文件
                //断开连接
                tcpSocket->disconnectFromHost();
                tcpSocket->close();

            }
        }
        }
    );
}

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

void ClientWidget::on_buttonConnect_clicked()
{
    //获取服务器的ip和端口
    QString ip = ui->lineEditIP->text();
    quint16 port = ui->lineEditPort->text().toInt();

    //主动和服务器连接
    tcpSocket->connectToHost(QHostAddress(ip), port);

    isStart = true;

    //设置进度条
    ui->progressBar->setValue(0);
}

6、tcp与udp对比

QT tcp与udp网络通信以及定时器的使用 (7)_第21张图片

7.总结

以上就是今天要讲的内容,本文简单介绍了QT的Tcp与udp网络通信。tcp传输文件的案列。以及定时器的多种用法

你可能感兴趣的:(QT,qt,C++)