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

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传输文件的案列。以及定时器的多种用法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1 月 28日算法练习-前缀和

小郑的蓝桥平衡串 思路&#xff1a;把 L 看成 1&#xff0c;Q 看成 -1&#xff0c;利用前缀和来得到输入串的前缀子串中LQ 的和&#xff0c;利用前缀和差的性质得到子串&#xff0c;通过枚举看它是否平衡。 将L看做1&#xff0c;Q看做&#xff0d;1&#xff0c;只有当某个区间…

【JS进阶】ES6箭头函数、forEach遍历数组

文章目录 前言一、箭头函数1.1 基本语法1.2 带参数的箭头函数1.3 this指针指向谁&#xff1f; 二、forEach遍历数组总结 前言 随着JavaScript语言的不断发展&#xff0c;ES6&#xff08;ECMAScript 2015&#xff09;引入了许多新的语法和特性&#xff0c;其中箭头函数和forEac…

【Linux】Linux权限的概念 -- 详解

一、Linux 中的用户 Linux 下有两种用户&#xff1a; 超级用户&#xff08;root&#xff09;&#xff1a;可以在 Linux 系统下做任何事情&#xff0c;不受限制。普通用户&#xff1a;在 Linux 下做有限的事情。 超级用户的命令提示符是 “#”&#xff0c;普通用户的命令提示符…

【Javaweb程序】【C00155】基于SSM的旅游旅行管理系统(论文+PPT)

基于SSM的旅游旅行管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于SSM的旅游旅行管理系统 本系统分为前台系统模块、管理员模块、用户模块以及商家模块 其中前台系统模块的权限为&#xff1a;当游客打开系统的网址后…

javax.servlet.http包

javax.servlet.http包 javax.srvlet.http包是对javax.servlet包的扩展。该包的类和接口处理使用HTTP进行通信的servlet。这些servlet也称为HTTP Servlet。您需要扩展HttpServlet类来开发HTTP Servlet。javax.servlet.http包经常使用的接口包括: HttpServletRequest接口HttpSe…

Flutter之环境搭建(小白教程)

这个章节我们学习如何安装Flutter&#xff0c;配置 Flutter 、Android Studio 环境&#xff0c;做开发的前置工作。 环境搭建有点麻烦&#xff0c;特别是Android环境的安装&#xff0c;没有代理简直就是噩梦&#xff0c;要有耐心一起加油&#xff01; Flutter 官网地址 一. 操…

ATT汇编

指令后缀 AT&T格式的汇编指令有不同的后缀 其中 b表示byte&#xff0c;字节 w表示word&#xff0c;字/两字节 l表示long&#xff0c;32位系统下的long是4字节 q表示quad&#xff0c;意味四重&#xff0c;表示4个字/8字节 寄存器用途 参见 AT&T的汇编世界 - Gemfield…

(自用)learnOpenGL学习总结-高级OpenGL-立方体贴图

ok终于来到了立方体贴图了&#xff0c;在这里面我们可以加入好看的天空包围盒&#xff0c;这样的画我们的背景就不再是黑色的了&#xff01; 首先&#xff0c;立方体贴图和前面的sampler2D贴图一样&#xff0c;不过是6个2D组成的立方体而已。 那么为什么要把6个组合在一起呢&…

81.网游逆向分析与插件开发-背包的获取-装备栏数据结构的逆向分析

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;自动化助手显示物品数据-CSDN博客 然后游戏中有弓箭&#xff0c;弓箭有数量&#xff0c;可以作为突破口&#xff0c;也可以使用物品id 获取弓的方式 获取弓箭的方式 然后搜索250 然后搜索出一个 然后…

【智能家居入门之微信小程序控制下位机】(STM32、ONENET云平台、微信小程序、HTTP协议)

实现微信小程序控制单片机外设动作 一、使用ONENET可视化组件控制单片机外设动作二、使用微信小程序控制单片机外设动作三、总结 本篇博客话接上文&#xff1a; https://blog.csdn.net/m0_71523511/article/details/135892908 上一篇博客实现了微信小程序接收单片机上传的数据…

《吐血整理》高级系列教程-吃透Fiddler抓包教程(30)-Fiddler如何抓取Android7.0以上的Https包-番外篇

1.简介 通过宏哥前边几篇文章的讲解和介绍想必大家都知道android7.0以上&#xff0c;有android的机制不在信任用户证书&#xff0c;导致https协议无法抓包。除非把证书装在系统信任的证书里&#xff0c;此时手机需要root权限。但是大家都知道root手机是非常繁琐的且不安全&…

开源大数据集群部署(七)Freeipa卸载

作者&#xff1a;櫰木 1、命令卸载 卸载FreeIPA服务器和客户端的命令&#xff0c;以及清理相关残留文件和卸载相关软件包。 ipa-server-install -U --uninstall #服务端卸载 ipa-client-install -U --uninstall #客户端卸载 #删除残留文件&#xff0c;避免二次安装失败 cd /va…

数据可视化工具JSON Crack结合内网穿透实现公网访问

文章目录 1. 在Linux上使用Docker安装JSONCrack2. 安装Cpolar内网穿透工具3. 配置JSON Crack界面公网地址4. 远程访问 JSONCrack 界面5. 固定 JSONCrack公网地址 JSON Crack 是一款免费的开源数据可视化应用程序&#xff0c;能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互…

Prometheus 监控系统的初步了解与系统搭建

目录 目录 目录 前言 Prometheus的相关知识 Prometheus特点 Prometheus的存储引擎&#xff1a;TSDB Prometheus的组件 1.核心组件&#xff1a;prometheus server Prometheus server又分为三个部分&#xff1a; 2.exports 3.client Library 4.cadvisor 5.blackbox-ex…

Java毕业设计-基于jsp+servlet的人事管理系统-第79期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于jspservlet的人事管理系统&#xff1a;有配套报告文档&#xff0c;前端 jsp、jquery&#xff0c;后端 servlet、jdbc&#xff0c;集成员工档案管理、公告浏览、请假信…

H5项目使用@microsoft/fetch-event-source

前言&#xff1a; 在microsoft/fetch-event-source 文档中&#xff0c;没有介绍在项目中使用script 引入&#xff0c;只介绍了通过npm 引入的方式&#xff1b;项目的接口又是 post 方式的流式接口。 解决方式&#xff1a; 借助 webpack 工具 实现步骤&#xff1a; 1、初始化…

vue预览pdf文件的几种方法

文章目录 vue预览pdf集中方法方法一&#xff1a;方法二&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a; 方法三&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a;自己调参数&#xff0c;选择符合自己的 vue预览pdf集中方法 我…

方差与协方差之间的区别?

方差和协方差都是用来衡量随机变量之间关系的统计量&#xff0c;但它们的计算方式和含义有所不同。 方差&#xff08;Variance&#xff09;&#xff1a;方差是描述数据集合离散程度的统计量&#xff0c;它衡量了数据点与均值之间的平均距离。 方差越大&#xff0c;表示数据点越…

【AI大模型应用开发】1.3 Prompt攻防(安全) 和 Prompt逆向工程

随着GPT和Prompt工程的大火&#xff0c;随之而来的是隐私问题和安全问题。尤其是最近GPTs刚刚开放&#xff0c;藏在GPTs后面的提示词就被网友们扒了出来&#xff0c;甚至直接被人作为开源项目发布&#xff0c;一点安全和隐私都没有&#xff0c;原作者的收益也必然受到极大损失……

图灵日记之java奇妙历险记--抽象类和接口

目录 抽象类概念抽象类语法 接口概念规则使用特性实现多个接口接口的继承接口使用实例Clonable接口和深拷贝抽象类和接口的区别 Object类 抽象类 概念 在面向对象的概念中,所有对象都是通过类来描述的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够…