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.mzph.cn/news/653664.shtml

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

相关文章

【ArcGIS微课1000例】0098:查询河流流经过的格网

本实验讲述,ArcGIS中查询河流流经过的格网,如黄河流经过的格网、县城、乡镇、省份等。 文章目录 一、加载数据二、空间查询三、结果导出四、注意事项一、加载数据 加载实验配套数据0098.rar中的河流(黄河)和格网数据,如下图所示: 接下来,将查询河流流经过的格网有哪些并…

SpringBoot将第三方的jar中的bean对象自动注入到ioc容器中

新建一个模块&#xff0c;做自动配置 config&#xff1a;需要准备两个类&#xff0c;一个自动配置类&#xff0c;一个配置类 CommonAutoConfig&#xff1a;此类用于做自动配置类它会去读取resoutces下的META-INF.spring下的org.springframework.boot.autoconfigure.AutoConfig…

LabVIEW探测器CAN总线系统

介绍了一个基于FPGA和LabVIEW的CAN总线通信系统&#xff0c;该系统专为与各单机进行系统联调测试而设计。通过设计FPGA的CAN总线功能模块和USB功能模块&#xff0c;以及利用LabVIEW开发的上位机程序&#xff0c;系统成功实现了CAN总线信息的收发、存储、解析及显示功能。测试结…

嘿嘿,vue之输出土味情话

有点好玩&#xff0c;记录一下。通过按钮调用网站接口&#xff0c;然后解构数据输出土味情话。 lovetalk.vue: <!--vue简单框架--> <template> <!-- 这是一个div容器&#xff0c;用于显示土味情话 --> <div class"talk"> <!-- 当点…

回归预测 | Matlab实现CPO-LSSVM【24年新算法】冠豪猪优化最小二乘支持向量机多变量回归预测

回归预测 | Matlab实现CPO-LSSVM【24年新算法】冠豪猪优化最小二乘支持向量机多变量回归预测 目录 回归预测 | Matlab实现CPO-LSSVM【24年新算法】冠豪猪优化最小二乘支持向量机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-LSSVM【24年…

K个一组翻转链表---链表OJ

https://leetcode.cn/problems/reverse-nodes-in-k-group/?envType=study-plan-v2&envId=top-100-liked K个一组进行翻转,大体上是和前面两两翻转是类似的,区别就在于,这里需要自己判断是否需要翻转,如何翻转,怎么记录。这里我们用递归来实现。 是否需要翻转…

数据结构·单链表经典例题

1. 移除链表元素 OJ链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 本题是说给出一个链表的头节点head和一个整数val&#xff0c;如果发现节点中存的数据有val就删掉它&#xff0c;最后返回修改后的链表头节点地址 如果题目中没有明确…

第十八讲_HarmonyOS应用开发实战(实现电商首页)

HarmonyOS应用开发实战&#xff08;实现电商首页&#xff09; 1. 项目涉及知识点罗列2. 项目目录结构介绍3. 最终的效果图4. 部分源码展示 1. 项目涉及知识点罗列 掌握HUAWEI DevEco Studio开发工具掌握创建HarmonyOS应用工程掌握ArkUI自定义组件掌握Entry、Component、Builde…

【Redis】关于它为什么快?使用场景?以及使用方式?为何引入多线程?

目录 1.既然redis那么快&#xff0c;为什么不用它做主数据库&#xff0c;只用它做缓存&#xff1f; 2.Redis 一般在什么场合下使用&#xff1f; 3.redis为什么这么快&#xff1f; 4.Redis为什么要引入了多线程&#xff1f; 1.既然redis那么快&#xff0c;为什么不用它做主数据…

Nginx 配置解析:从基础到高级应用指南

Nginx 配置解析&#xff1a;从基础到高级应用指南 Nginx 配置解析&#xff1a;从基础到高级应用指南1. 安装和基本配置安装 Nginx基本配置 2. 虚拟主机配置3. 反向代理配置4. 负载均衡配置5. SSL 配置6. 高级配置选项结语 Nginx 配置解析&#xff1a;从基础到高级应用指南 Ngi…

C#使用OpenCvSharp4库读取电脑摄像头数据并实时显示

一、OpenCvSharp4库 OpenCvSharp4库是一个基于.Net封装的OpenCV库&#xff0c;Github源代码地址为&#xff1a;https://github.com/shimat/opencvsharp&#xff0c;里面有关于Windows下安装OpenCvSharp4库的描述&#xff0c;如下图所示&#xff1a; 二、C#使用OpenCvSharp4库…

Python tkinter (8) ——Spinbox控件

Python的标准Tk GUI工具包的接口 tkinter系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Python tkinter (2) —— Button标签 Python tkinter (3) —— Entry标签 Python tkinter (4) —— Text控件 Python tkinter (5) 选项按钮与复选框 Pyt…

中间件安全

中间件安全 vulhub漏洞复现&#xff1a;https://vulhub.org/操作教程&#xff1a;https://www.freebuf.com/sectool/226207.html 一、Apache Apache(音译为阿帕奇)是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上&#xff0c;由于其跨平台和…

RT-DETR改进有效系列目录 | 包含卷积、主干、RepC3、注意力机制、Neck上百种创新机制

💡 RT-DETR改进有效系列目录 💡 前言 Hello,各位读者们好 Hello,各位读者,距离第一天发RT-DETR的博客已经过去了接近两个月,这段时间里我深入的研究了一下RT-DETR在ultralytics仓库的使用,旨在为大家解决为什么用v8的仓库训练的时候模型不收敛,精度差的离谱的问题,…

智能小程序页面配置、运行机制及路由

页面介绍 Page 代表应用的一个页面&#xff0c;负责页面展示和交互。每个页面对应一个子目录&#xff0c;一般有多少个页面&#xff0c;就有多少个子目录。它也是一个构造函数&#xff0c;用来指定页面的初始数据、生命周期回调、事件处理函数等。 每个小程序页面一般包含以下…

MarkDown快速入门-以Obsidian编辑器为例

直接上图&#xff0c;左右对应。 首先是基础语法。 # 标题&#xff0c;几个就代表几级标题&#xff1b;* 单个是序号&#xff0c;两个在一起就是斜体&#xff1b;- [ ] 代表任务&#xff0c;注意其中的空格&#xff1b; 然后是表格按钮代码 | 使用中竖线代表表格&#xff0c…

Ubuntu22.04安装docker

君衍. 一、认识docker二、查看Docker的依赖三、在Ubuntu22.04上安装Docker步骤1、更新Ubuntu2、添加Docker库3、安装Docker4、Docker测试 四、安装docker-compose 一、认识docker Docker是一个软件容器平台&#xff0c;属于Linux容器的一种封装&#xff0c;同时提供简单易用的…

3. 构建以太网交换网络

3.1 实验一&#xff1a;以太网基础与 VLAN 配置实验 3.1.1 实验介绍 3.1.1.1 关于本实验 以太网是一种基于 CSMA/CD&#xff08;Carrier Sense Multiple Access/Collision Detection&#xff09;的共享通讯介质的数据网络通讯技术。当主机数目较多时会导致冲突严重、广播泛滥…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-友情链接管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…