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、 客户端的流程
总体双方的流程如下:
3、服务器的编写
完成的结果如下:客户端点击连接服务器按钮。成功连接就可以相互进行通信了。
注意,要使用QT的网络模块,需要在.pro文件中添加QT += network声明,以添加网络模块的依赖。
- 声明TCP监听套接字与通信套接字
#include <QTcpServer>
#include <QTcpSocket>
QTcpServer *pTcpListenSocket = nullptr;
QTcpSocket *pTcpCommunicatSocket = nullptr;
- 创建QTcpServer套接字。QTcpSocket套接字在客户端成功连接时获取。此时为空。
pTcpListenSocket = new QTcpServer(this);
pTcpCommunicatSocket = nullptr;
- 监听客户端连接请求并处理
pTcpListenSocket->listen(QHostAddress::Any, 7777); /* 服务器地址和端口 */
- 获取通信套接字
/* 取出建立好的连接套接字 */
pTcpCommunicatSocket = pTcpListenSocket->nextPendingConnection();
- 最后就可以进行发送接收数据
/* 发送给客户端数据接口 */
pTcpCommunicatSocket->write(sendData);
/* 接收客户端的数据接口 */
QByteArray arry = pTcpCommunicatSocket->readAll();
- 断开连接
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、客户端的编写
- 声明TCP通信套接字
#include <QTcpSocket>
QTcpSocket *pTcpClientSocket = nullptr;
- 创建TCP通信套接字
pTcpClientSocket = new QTcpSocket(this);
- 主动与服务器建立连接
/* 主动与服务器建立连接 */
pTcpClientSocket->connectToHost(QHostAddress(ip), port);
- 读写数据
/* 发送数据给服务器 */
pTcpClientSocket->write(sendData);
/* 接收服务器的数据 */
QByteArray arry = pTcpClientSocket->readAll();
- 断开连接
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界面:
- 声明UDP通信套接字
#include <QUdpSocket>
QUdpSocket *pUdpSocket = nullptr;
- 创建UDP通信套接字
pUdpSocket = new QUdpSocket(this);
- 绑定端口
pUdpSocket->bind(QHostAddress::Any,7777);
- 读写数据
/* 获取数据 */
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);
- 启动定时器
timer->start(1000)
3 . 连接信号槽
当start启动定时器就会每隔设定的时间触发timeout信号
connect(timer, &QTimer::timeout,
[=]()
{
/* 定时器时间到的处理 */
}
);
- 停止计时
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);
- 定时器时间到进入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);
}
}
- 关闭定时器
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传输文件的案列。以及定时器的多种用法