Tcp传输文件原理图:
运行结果图: ClientWidget.h
#include <QWidget>
#include <QTcpSocket>
#include <QFile>
namespace Ui {
class ClientWidget;
}
class ClientWidget : public QWidget
{
Q_OBJECT
public:
explicit ClientWidget(QWidget *parent = nullptr);
~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>
#include <QProgressBar>
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
tcpSocket = new QTcpSocket(this) ;
ui->progressBar->setValue(0);
setWindowTitle("客户端");
isStart = true ; //若默认正确
connect(tcpSocket,&QTcpSocket::connected,
[=]()
{
qDebug() << "连接成功。" ;
}
) ;
/*等待别人发东西过来**/
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
//取出接受的内容
QByteArray buf = tcpSocket->readAll() ;
//区分到底是头还是文件数据,因此需要一个标志位进行确认
if(true == isStart)
{
//接受头
isStart = false ;
//解析头部信息需要使用字符串拆包函数
/*拆包函数section中第一个参数为辨识符,用以区分分割点,第二个参数为开始段数,第三个参数为结束段数**/
//初始化
fileName = QString(buf).section("##",0,0) ; //从第0到第0段若string 为 “hello##1024” 则00代表hello 010代表hello1024
fileSize = QString(buf).section("##",1,1).toInt() ;
recvSize = 0 ;
//打开文件
file.setFileName(fileName);
bool isOk = file.open(QIODevice::WriteOnly) ;
if(false == isOk)
{
qDebug() << "WriteOnly error!" ;
}
//弹出对话框,显示接受文件信息
QString str = QString("[%1:%2kb]").arg(fileName).arg(fileSize/1024) ;
QMessageBox::information(this,"文件信息",str) ;
//设置进度条
//进度条需要设定当前值,最小值与最大值
ui->progressBar->setMaximum(fileSize/1024);
ui->progressBar->setMinimum(0);
ui->progressBar->setValue(0);
}else //文件信息
{
qint64 len = file.write(buf) ; //传多少写多少
if(len > 0)
{
//客户端接收多少
//就把接收的数据发给服务器,以便更新服务器的进度条
recvSize += len ; //累计接受大小
QString temp = QString::number(recvSize) ;
tcpSocket->write(temp.toUtf8().data()) ;
qDebug() << "temp"<<temp ;
}
//累计完接受大小之后更新进度条
ui->progressBar->setValue(recvSize/1024);
if(recvSize == fileSize) //接受完毕之后弹出对话款提示接受成功,并且关闭文件
{
//先给服务器发送(接受文件完成信息)
tcpSocket->write("#"); //提示接受文件完成
//接受完成之后则各种完成的老年操作
file.close();
QMessageBox::information(this,"成功","传输数据成功") ;
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);
}
ServerWidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字
#include <QFile>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE
class ServerWidget : public QWidget
{
Q_OBJECT
public:
ServerWidget(QWidget *parent = nullptr);
~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 ;//定义定时器
};
#endif // SERVERWIDGET_H
ServerWidget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
#include <QFileDialog>
#include <QDebug>
#include <QFileInfo> //获取文件信息!!!!
#include <QTimer>
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);
/*为了客户端那边接完文件之后在提醒服务器端文件发送完毕**/
/*如果客户端成功和服务器连接套接字会自动触发newconnection()**/
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
/*取出通信套接字,建立好连接的套接字**/
tcpSocket = tcpServer->nextPendingConnection() ;
//获取对方的IP和端口
//peerAdress表示的是对方的地址
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 array = tcpSocket->readAll() ;
if(QString(array).back() == "#")
{
ui->textEdit->append("文件发送完毕");
file.close();
tcpSocket->disconnectFromHost(); //断开客户端
tcpSocket->close();
}else //未接收完毕则打印值
{
qDebug() <<"array"<< array ;
}
}
);
}
) ;
connect(&timer,&QTimer::timeout, //注意这里connect中的内容为取地址
[=]()
{
//因为只起20ms的定时作用
//因此进来之后立马关闭定时器
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;
sendSize = 0;
//获取文件信息
QFileInfo info(filePath) ; //括号中的内容怎么可以忘记呢!!!????
fileName = info.fileName() ; //获取文件名字
fileSize = info.size() ; //获取文件大小
//只读方式打开文件
//指定文件的名字
file.setFileName(filePath);
//打开文件
bool isOk = file.open(QIODevice::Truncate|QIODevice::ReadOnly) ;
if(false == isOk)
{
qDebug() << "只读方式打开文件失败。" ;
}
//提示打开文件路径
ui->textEdit->append(filePath);
/*选完之后便不能选了**/
ui->ButtonFile->setEnabled(false);
ui->ButtonSend->setEnabled(true);
}else
{
qDebug() << "选择文件路径无效。88" ;
}
}
//发送文件按钮
void ServerWidget::on_ButtonSend_clicked()
{
//先发送文件头信息 文件名##文件大小
QString head = QString("%1##%2").arg(fileName).arg(fileSize) ;
//发送头部信息
qint64 len = tcpSocket->write(head.toUtf8().data()) ;
if(len > 0) //说明头部信息发送成功
{
//防止黏包问题,因此延时需要延时20ms
//发送真正的文件信息
timer.start(20); //20ms之后发送文件
}else
{
qDebug() << "头部信息发送失败。" ;
file.close();
/*因为传输失败,因此需要重新打开新文件所以文件按钮点亮,发送按钮熄灭**/
ui->ButtonFile->setEnabled(true);
ui->ButtonSend->setEnabled(false);
}
}
void ServerWidget::sendData()
{
qint64 len = 0;
do
{
//每次发送数据的大小为4K
char buf[4*1024] = {0} ;
//往文件中读数据
len = file.read(buf,sizeof(buf)) ;
//发送数据,读多少发多少。
len = tcpSocket->write(buf,len) ; //读len 发len 所以是读多少发多少
//最后再把发的尺寸赋值给len
sendSize += len ;
}while(len > 0) ; //最好的判断条件!!
//是否发送文件完毕
if(sendSize == fileSize)
{
ui->textEdit->append("文件正在发送...");
file.close();
}
}