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

news2024/11/19 4:57:38

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协议的通信等。
下面是继承关系:
在这里插入图片描述

2、QT TCP通信

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

1、 服务器的流程

在这里插入图片描述

2、 客户端的流程

在这里插入图片描述
总体双方的流程如下:
d

3、服务器的编写

完成的结果如下:客户端点击连接服务器按钮。成功连接就可以相互进行通信了。
在这里插入图片描述

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

  1. 声明TCP监听套接字与通信套接字
#include <QTcpServer>
#include <QTcpSocket>
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的布局:
在这里插入图片描述
serveridget.h

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

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 <QPushButton>
#include <QDebug>

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>
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的布局:
在这里插入图片描述
客户端的创建:
在这里插入图片描述
在这里插入图片描述

clientwidget.h

#include <QWidget>
#include <QTcpSocket>
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 <QPushButton>
#include <QHostAddress>

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 <QApplication>
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、客户端流程

在这里插入图片描述
创建其它的客户端也是一样的流程。
总体双方的流程如下:
在这里插入图片描述

2、客户端编写

注意,要使用QT的网络模块,需要在.pro文件中添加QT += network声明,以添加网络模块的依赖。
最后的效果如下:
在这里插入图片描述

客户端的ui界面:
在这里插入图片描述

  1. 声明UDP通信套接字
#include <QUdpSocket>
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 <QWidget>
#include <QUdpSocket>
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 <QPushButton>
#include <QDebug>

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广播

在这里插入图片描述

4、UDP组播

在这里插入图片描述
注意组播在绑定是IP要选择QHostAddress::AnyIPv4 并且组播是D类地址。
udpSocket->leaveMulticastGroup(QHostAddress(“224.0.0.2”)) 如果不想接收也可以退出这个组播。

4、定时器的用法

1、方法一

最后的效果:
在这里插入图片描述
只是简单使用两个不同的定时器。
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 <QWidget>
#include <QTimer>
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 <QWidget>
#include <QTimerEvent>

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的网络模块,需要在.pro文件中添加QT += network声明,以添加网络模块的依赖。

1、服务器编写

服务器ui的设计:
在这里插入图片描述
serverwidget.h

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>

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 <QFileDialog>

#include <QFileInfo>

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的设计:
在这里插入图片描述
功能:当客户端与服务器成功连接后。服务器选择发送一个文件给客户端。
clientwidget.h

#include <QWidget>
#include <QTcpSocket>
#include <QFile>

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 <QDebug>
#include <QMessageBox>
#include <QHostAddress>

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对比

在这里插入图片描述

7.总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1417230.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode刷题日志-146LRU缓存

思路&#xff1a;使用hashmap储存key&#xff0c;vaule&#xff0c;使用双向链表以快速查到尾结点&#xff08;待逐出的节点&#xff09;&#xff0c;链表的题一定要在纸上画一下&#xff0c;不然连着连着就不知道连在哪里去了 class LRUCache {public class ListNode {int ke…

【js基础】日期对象的使用,查找、增加、克隆、删除DOM节点,M端事件

文章目录 前言一、日期对象日期对象的作用1.1 实例化1.2 日期对象的方法1.3 时间的格式化1.4 时间戳的使用时间戳是什么js的时间戳 二、DOM的增删改查什么叫做DOM节点2.1 DOM的查找2.2 增加节点2.3 克隆节点和删除节点 三、M端事件3.1 M端是什么&#xff1f; 总结 前言 在 Jav…

你的MiniFilter安全吗?

简介 筛选器管理器 (FltMgr.sys)是Windows系统提供的内核模式驱动程序, 用于实现和公开文件系统筛选器驱动程序中通常所需的功能; 第三方文件系统筛选器开发人员可以使用FltMgr的功能可以更加简单的编写文件过滤驱动, 这种驱动我们通常称为MiniFilter, 下面是MiniFilter的基本…

STM32CubeMX教程31 USB_DEVICE - HID外设_模拟键盘或鼠标

目录 1、准备材料 2、实验目标 3、模拟鼠标实验流程 3.0、前提知识 3.1、CubeMX相关配置 3.1.0、工程基本配置 3.1.1、时钟树配置 3.1.2、外设参数配置 3.1.3、外设中断配置 3.2、生成代码 3.2.0、配置Project Manager页面 3.2.1、设初始化调用流程 3.2.2、外设中…

优选6款前端动画特效分享(附在线演示)

优选6款前端动画特效 其中有CSS动画、canvas动画、js小游戏等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 翻页时钟特效 基于react实现的一款翻页时钟特效 切换时间特效跟比赛切换分数牌相似 以下效果图只能体现…

Gitlab7.14 中文版安装教程

Gitlab7.14 中文版安装教程 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-01-28csdn 博客名称&#xff1a;五维空间-影子&#xff0c;欢迎关注 …

T05垃圾收集算法与垃圾收集器ParNew CMS

垃圾收集算法与垃圾收集器ParNew & CMS 垃圾收集算法 #### f 分代收集理论 当前虚拟机的垃圾收集都采用分代收集算法。根据对象存活周期不同将内存分为几块&#xff0c;一般将java堆分为新生代和老年代&#xff0c;然后根据各个年代的特点选择不同的垃圾收集算法。 在新…

MySQL-窗口函数 简单易懂

窗口函数 考查知识点&#xff1a; • 如何用窗口函数解决排名问题、Top N问题、前百分之N问题、累计问题、每组内比较问题、连续问题。 什么是窗口函数 窗口函数也叫作OLAP&#xff08;Online Analytical Processing&#xff0c;联机分析处理&#xff09;函数&#xff0c;可…

ESP8266 控制之 : 使用 RingBuffer USART1 和 USART3互传

简介 使用Buffer来避免数据的丢失, 或许你自己在使用串口进行收发时会丢失数据, 现在我们就来简单使用一下RingBuffer创建Rx、Tx的Buffer来避免发送接收丢包或数据丢失问题。 扩展知识 RingBuffer的介绍, 看完大概也就知道了&#xff0c;实在不知道就看看下面的代码 线路连接…

微信小程序开发学习笔记《14》上拉触底事件案例

微信小程序开发学习笔记《14》上拉触底事件案例 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读对应官方文档 一、最终实现效果 实现在加载完这一页之后&#xff0c;刷新之后展示 “数据加载中”&#xff0c;加载完后…

超温报警器电路设计方案汇总

超温报警器电路设计方案&#xff08;一&#xff09; 该超温报警电路由温度采集电路、继电器控制电路、延时电路、秒脉冲信号发生器、计数译码电路、数显电路、报警电路共同构成。下面来详细介绍一下各部分电路的功能。 温度采集电路 温度采集电路由负温度系数的热敏电阻RW、R…

STM32 自学笔记 学习笔记 一

起源&#xff0c;A7,A9,M3&#xff0c;原来弄了A9的TQ2440&#xff0c;结果还得来重新熟悉下32函数JLINK使用SW方式&#xff0c;本来可以下载&#xff0c;但是一根线掉了重新上去&#xff0c;就出各种跟线无关问题&#xff0c;干脆把32断了重新接&#xff0c;结果就成功了&…

服务攻防-开发组件安全JacksonFastJson各版本XStreamCVE环境复现

知识点 1、J2EE-组件Jackson-本地demo&CVE&#xff08;数据处理&#xff09; 2、J2EE-组件FastJson-本地demo&CVE&#xff08;数据处理&#xff09; 3、J2EE-组件XStream-本地demo&CVE&#xff08;数据处理&#xff09; 章节点&#xff1a; 1、目标判断-端口扫描…

【机器学习入门】18种常见的机器学习算法数学公式及解析

机器学习算法基础原理&#xff1a; https://codeknight.blog.csdn.net/article/details/135632808https://codeknight.blog.csdn.net/article/details/135632808 https://codeknight.blog.csdn.net/article/details/135639843https://codeknight.blog.csdn.net/article/detai…

Android 性能优化总结:包体积优化

前言 随着开发不断迭代&#xff0c;App体积越来越大&#xff0c;包大小的增大也会给我们应用带来其他的影响 比如 下载率影响 过大的包体积会影响下载转化率&#xff0c;根据Google Play Store包体积和转化率分析报告显示&#xff0c;平均每增加1M&#xff0c;转化率下降0.2%左…

【MATLAB第95期】#源码分享 | 基于MATLAB的卷积神经网络CNN图像分类源代码分享(含两个案例)

【MATLAB第95期】#源码分享 | 基于MATLAB的卷积神经网络CNN图像分类源代码分享&#xff08;含两个案例&#xff09; 一、案例一 1、背景介绍 目的&#xff1a;训练和测试卷积神经网络&#xff0c;以检测钻头三种类型。 深度学习&#xff08;DL&#xff09;是机器学习的一个子…

Element table组件内容\n换行

漂亮的页面总是让人心旷神怡&#xff0c;层次清晰的页面让用户操作起来也是易于上手及展示。 如下的页面展示就是非常low的&#xff1a;用户根本阅读其中的数据。 在这个页面&#xff0c;根据用户填写过程生成多次填写记录&#xff0c;如果不进行层次性的展示&#xff0c;数据…

【C语言】学生管理系统

学生管理系统是一个用于管理学生信息、成绩、课程等数据的软件系统。在本文中&#xff0c;我们将使用C语言来实现一个简易的学生管理系统&#xff0c;包括学生信息的录入、显示、查询等功能。我们将使用文件来存储学生信息&#xff0c;以便实现持久化存储。 该学生管理…

JAVA 学习 面试(十一)常见设计模式

设计模式 ## 1、创建型模式 对象实例化的模式&#xff0c;创建型模式用于解耦对象的实例化过程。 单例模式&#xff1a;某个类智能有一个实例&#xff0c;提供一个全局的访问点。 工厂模式&#xff1a;一个工厂类根据传入的参量决定创建出哪一种产品类的实例。 抽象工厂模式&a…

Android学习之路(25) Theme和Style

1、官方详细解读 样式和主题背景 | Android 开发者 | Android Developers 2、应用场景 类似web设计中css样式。将应用设计的细节与界面的结构和行为分开。 样式style &#xff1a;应用于 单个 View 的外观。样式可以指定字体颜色、字号、背景颜色等属性 主题theme&…