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_NAMESPACEclass Serveridget : public QWidget
{Q_OBJECTpublic: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_OBJECTpublic: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_OBJECTpublic: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_NAMESPACEclass 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_NAMESPACEclass MyWidget : public QWidget
{Q_OBJECTpublic: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_OBJECTpublic: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{//每次发送数据的大小 4Kchar 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:防止文件太大越界 / 1024if(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.hqwc.cn/news/437748.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity学习之坦克游戏制作(3)结束场景的制作

文章目录 1 通关输入玩家名1.1 设置UI1.2 增加脚本 2 失败面板3 优化鼠标处于游戏内锁定状态4 项目打包 1 通关输入玩家名 由于排行榜功能&#xff0c;先在GameScene界面提供玩家一个输入玩家名以便保存到排行榜的界面。&#xff08;如果有割裂感建议从1开始看&#xff09; 1…

解锁多模态独特魅力-“机器人+Agent+多传感器融合+3DLLM”诠释终极组合大招!

01-Multiply算法背景 01.01-触觉传感器 触觉传感器是一种用于感知和测量物体接触力、形状、纹理和其他相关参数的传感器。它们模拟人类触觉系统&#xff0c;通过收集和解释物体与传感器之间的相互作用来获取信息。工作原理&#xff1a;触觉传感器使用不同的原理来感知接触力和…

云微呼云呼叫中心:颠覆传统客户服务模式的数字化创新

1. 引言 随着云计算技术的不断成熟和普及&#xff0c;云呼叫中心作为一种全新的客户服务解决方案&#xff0c;正在以其高效、灵活和智能的特点&#xff0c;逐渐颠覆传统的客户服务模式。本文将深入探讨云呼叫中心的定义、特点、优势以及对企业的重要意义。 2. 云呼叫中心的定…

万能门店小程序功能源码系统 带完整的安装代码包以及搭建教程

线上流量的日益枯竭&#xff0c;线下门店面临着巨大的竞争压力。为了在激烈的市场竞争中脱颖而出&#xff0c;门店需要借助数字化手段提升用户体验、优化经营管理。而小程序作为一种轻量级的应用程序&#xff0c;具有无需下载安装、即用即走的特点&#xff0c;非常适合线下门店…

Redis核心技术与实战【学习笔记】 - 4.Redis 切片集群

前言 如果 Redis 要存 5000 万个键值对&#xff0c;每个键值对大约 512B&#xff0c;那么该如何选择主键的内存容量呢&#xff1f; 粗略估算下&#xff0c;所有键值对需要内存空间大约是 25GB。我们很自然的想到第一个方案&#xff0c;选择一台 32GB 的内存的主机来部署 Redi…

江科大stm32学习笔记6——GPIO输入准备

一、按键消抖 由于按键内部使用的是机械式弹簧片&#xff0c;所以在按下和松开时会产生5~10ms的抖动&#xff0c;需要通过代码来进行消抖。 二、滤波电容 在电路中&#xff0c;如果见到一端接在电路中&#xff0c;一端接地的电容&#xff0c;则可以考虑它的作用为滤波电容&am…

2023年:个人年度成长与团队协作成就

文章目录 个人职业发展的喜悦团队成就的辉煌公众号CSDN申请了移动安全领域新星创作者获得6月城市之星北京TOP 10获得23年博客之星TOP 41年度总结 知识星球 开拓新领域的决心免费知识大陆付费知识大陆 展望未来福利时间知识星球会员一年知识星球立减88券 在这个充满挑战与机遇的…

【使用New Bing的Dall-e-3绘画(完全免费,无需账号)】

前言 New Bing 的 DALL-E-3 是一款由 OpenAI 开发的 AI 绘画工具&#xff0c;它能够根据用户提供的文本提示生成高质量的图片。与其他 AI 绘画工具不同&#xff0c;DALL-E-3 最大的特点在于能够生成更精确的英文字符。此外&#xff0c;DALL-E-3 还具有以下优点&#xff1a; 1…

根据两个主键查询变更日志 抽屉时间线降序

时间戳例子&#xff1a; <div class"block"><el-timeline><el-timeline-item timestamp"2018/4/12" placement"top"><el-card><h4>更新 Github 模板</h4><p>王小虎 提交于 2018/4/12 20:46</p>…

浅析透明图片显示

1、理解图片构成 上面是一个飞机的透明图片&#xff0c;每个图片都是有一个个像素点构成的&#xff0c;每个像素点都是一个颜色&#xff0c;在内存中占4个字节&#xff0c;由透明度、红、绿、蓝构成。如下图&#xff1a; 该飞机图片飞机图片长51像素&#xff0c;宽63像素。就是…

echarts多个折线图共用X轴,实现tooltip合并和分离

echarts共享X轴案例&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</…

STM32之IIC总线控制ATC24C04

一、存储器介绍 1、电子密码存储概述 单片机的电子密码存储是一种将密码信息以电子形式存储在单片机内部的技术。它通常用于需要保护敏感信息或限制访问权限的应用程序&#xff0c;如安全系统、门禁系统、电子锁等。 电子密码存储可以通过多种方式实现&#xff0c;以下是其中…