传输文件和传输信息的区别:
- 传输信息,只是一条数据,传输文件是多条数据
- 传输信息传输过去一般都会显示,传输文件一般不会显示,一般只是存放在文件中
- 传输文件需要传输,文件大小和文件名称(不然不知道什么时候结束)
在这里使用的是服务器端发送数据,客户端接收数据。
为了在本地测试把服务器端和客户端分项目编写,方便在一台电脑上测试。
总的代码会在文件最后给出
目录显示:
- File_Tcp为服务器端,发送文件
- File_Tcp_Socket 为客户端,接收文件(创建的时候忘记该类名了,这里就使用widget了)
服务器端的编写
发送的规则:
- 先发送,文件的参数,文件大小和文件名
- 然后再发送,文件的内容
创建一个项目;
pro文件中添加:
QT +=network
ui界面添加以下控件:
tcp_server.h文件内容解析
1.添加以下头文件:
#include<QTcpServer>
#include<QMessageBox>
#include<QFile>
#include<QMessageBox>
#include<QTcpSocket>
#include<QHostAddress>
#include<QNetworkInterface>
#include<QFileDialog>
#include<QDataStream>
#include<QFileInfo>
2.添加私有成员:
private:
QTcpServer *server;//服务器端
QTcpSocket *socket;//套接字
QString FilePathName;//文件路径
QString FileName;//文件名
QFile * LocalFile;//文件指针,打开需要传输的文件
qint64 FileSize=0;//文件总大小
qint64 TFSize=0;//已传输的大小
qint64 ToBeSize=0;//未传输的数据
qint64 payloadSize=64*1024;//有效的载荷,64KB
QByteArray block;//缓存,用来临时存储需要发送的信息
3.槽函数
private slots:
void on_pushButton_3_clicked();
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_4_clicked();
//上面是ui界面转到槽生成的信号
void Send_File(qint64 num);//用来传输文件内容
4.事件和其他函数:
protected:
void closeEvent(QCloseEvent *event);//重写关闭事件
QString getIp();//获取IP
tcp_server.cpp文件内容解析
1.构造函数中的内容
server=new QTcpServer(this);//QTcpServer的初始化
QString Ip=getIp();//获取本机的IP地址
ui->lineEdit_4->setText(tr("%1").arg(Ip));//再主机的lineEdit中显示IP地址
ui->lineEdit_5->setText("6666");//设置端口号
socket=nullptr;//初始化QTcpSocket
connect(server,&QTcpServer::newConnection,[=]()//当有新的连接时
{
QMessageBox::information(this,"提示信息","已有新连接",QMessageBox::Ok);
socket=new QTcpSocket(this);//套接字
socket=server->nextPendingConnection();//获取套接字
});
//实现可以多次传输,当文件地址改变时,这些参数都置为0
connect(ui->lineEdit,&QLineEdit::textChanged,[=]()//
{
FileSize=0;//文件总大小
TFSize=0;//已传输的大小
ToBeSize=0;//未传输的数据
ui->lineEdit_2->setText("");
ui->lineEdit_3->setText("");
});
2.getIP()函数的实现
QString TCP_Server::getIp()//获取IP
{
QList<QHostAddress> addss=QNetworkInterface::allAddresses();//获取全部地址
foreach(QHostAddress add,addss)//遍历这些地址
{
if(add.protocol()==QAbstractSocket::IPv4Protocol)//如果地址为IVP4的话
{
return add.toString();//返回该地址
}
}
return 0;//没有的话返回0
}
3.监听的实现:
void TCP_Server::on_pushButton_3_clicked()//监听
{
if(!server->listen(QHostAddress(ui->lineEdit_4->text()),ui->lineEdit_5->text().toInt()));//如果监听失败
{
return;
}
}
4.获取文件名的实现:
void TCP_Server::on_pushButton_clicked()//获取文件名
{
FilePathName=QFileDialog::getOpenFileName(this);//获取路径
if(!FilePathName.isEmpty())//文件名不为空
{
QFileInfo fi(FilePathName);//创建一个文件信息对象
FileName=fi.fileName();//获取文件名
ui->lineEdit->setText(FilePathName);//放置文件名
}
}
5.取消的实现:
void TCP_Server::on_pushButton_4_clicked()//取消
{
if(server->isListening())//如果正在监听
{
server->close();
if(LocalFile->isOpen())
{
LocalFile->close();
}
socket->abort();//断开连接
}
}
6.重写关闭事件:
void TCP_Server::closeEvent(QCloseEvent *event)//重写关闭事件
{
on_pushButton_4_clicked();//取消
close();
}
7.发送的实现,(发送文件的一些参数)
void TCP_Server::on_pushButton_2_clicked()//发送文件信息
{
LocalFile=new QFile(FilePathName);//打开该文件
if(!LocalFile->open(QFile::ReadOnly))//当文件打不开
{
QMessageBox::information(this,"提示信息","发送失败",QMessageBox::Ok);
return;
}
//当接收完一次数据后,执行传输文件的操作,直到传输完
connect(socket,SIGNAL(bytesWritten(qint64)),this,SLOT(Send_File(qint64)));
//使用缓存
FileSize=LocalFile->size();//获取文件的大小
QDataStream Ds(&block,QIODevice::WriteOnly);//创建一个数据流
Ds.setVersion(QDataStream::Qt_5_9);//设置版本号;
Ds<<qint64(0)<<qint64(0)<<FileName;//将数据写入流中
FileSize+=block.size();//获取流中数据的大小
Ds.device()->seek(0);//回到开头
Ds<<FileSize<<qint64(block.size()-sizeof(qint64)*2);//数据的总长度和文件长度
//把文件头传输出去,同时修改待发送的字节数
ToBeSize=FileSize-socket->write(block);
block.resize(0);//把缓存置0,以便下次使用
}
这里主要解释一下
//当接收完一次数据后,执行传输文件的操作,直到传输完
connect(socket,SIGNAL(bytesWritten(qint64)),this,SLOT(Send_File(qint64)));
在这里,使用新版的信号于槽,会报错,不知道什么原因。
bytesWritten(qint64):每次将数据有效负载写入设备的当前写入通道时,都会发出此信号。会返回写入的字节数。
当写入通道时,使用Send_File(qint64)来统计字节数
8.Send_File(qint64)的实现
- 首先要统计数据
- 判断数据,如果还有未传输的数据,继续传输
- 在显示总的文件大小和已传输的大小
- 如果已发送的文件大小==总文件大小,关闭文件关闭服务器,提示传输完成
void TCP_Server::Send_File(qint64 num)//传输文件数据
{
qApp->processEvents();//防止传输文件时页面冻结
TFSize+=(int)num;//更新已传输的数据大小
if(ToBeSize>0)//如果未传输的数据大于0
{
//当剩下的数据小于payloadSize时,优先使用更小的值
block=LocalFile->read(qMin(ToBeSize,payloadSize));//读取数据
ToBeSize-=(int)socket->write(block,block.size());//发送并更新数据大小
block.resize(0);//清空缓存
}
else
{
LocalFile->close();
}
ui->lineEdit_3->setText(tr("%1").arg(FileSize/(1024*1024))+"MB");//总的数据量
ui->lineEdit_2->setText(tr("%1").arg(TFSize/(1024*1024))+"MB");//已传输的数据
if(FileSize==TFSize)
{
LocalFile->close();//关闭文件
server->close();//关闭服务器
QMessageBox::information(this,"提示信息","传输完成",QMessageBox::Ok);
}
}
main函数:
#include "tcp_server.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TCP_Server w;
w.show();
return a.exec();
}
服务器端的搭建
接收文件时,需要判断是否接受了文件信息
- 接受了话再接受文件的内容
创建一个项目:
pro文件中添加:
QT +=network
ui界面中添加以下控件:
widget.h文件的内容解析
1.添加以下头文件
#include<QTcpSocket>
#include<QDataStream>
#include<QMessageBox>
#include<QHostAddress>
#include<QFile>
2.添加私有成员
QTcpSocket *socket;//套接字
QFile *file;//文件指针
qint64 RCDsize;//接收的数据
qint64 ToBoSize;//未接收的数据
qint64 Filesize;//文件总大小
qint64 Allsize;//总大小
QString FileName;//文件名称
QByteArray inblock;//缓存
3.槽函数
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
//以上未ui界面中转到槽实现
void readData();//读取数据
4.重写事件
protected:
void closeEvent(QCloseEvent *event);//重写关闭事件
widget.cpp文件的内容解析:
1.构造函数
socket=new QTcpSocket(this);
ui->lineEdit_2->setText("6666");//设置端口号未6666
ToBoSize=0;
Filesize=0;
RCDsize=0;
connect(socket,&QTcpSocket::readyRead,this,&Widget::readData);//读取数据
2.连接的实现
void Widget::on_pushButton_clicked()//连接
{
socket->abort();//断开已有连接
//创建连接
socket->connectToHost(QHostAddress
(ui->lineEdit->text()),ui->lineEdit_2->text().toInt());
}
//QHostAddress(ui->lineEdit->text())为地址
//ui->lineEdit_2->text().toInt()为端口号
3.取消的实现
void Widget::on_pushButton_2_clicked()//取消
{
socket->abort();//断开连接
if(file->isOpen())
{
file->close();
}
}
4.重写关闭事件
void Widget::closeEvent(QCloseEvent *event)
{
socket->abort();
if (file->isOpen())
file->close();
close();
}
5.读取数据的实现
void Widget::readData()//读取数据
{
QDataStream in(socket);//流读取数据
in.setVersion(QDataStream::Qt_5_9);
if(RCDsize<=sizeof(qint64)*2)
{
if((socket->bytesAvailable()>=sizeof(qint64)*2)
&&(Filesize==0))
{
in>>Allsize>>Filesize;//读取资源的大小,文件的大小
RCDsize+=sizeof(qint64)*2;//更新读取的数据
}
if((socket->bytesAvailable()>=Filesize)&&
(Filesize!=0))
{
in>>FileName;//读取文件名称
file=new QFile(FileName);//打开文件
ui->lineEdit_3->setText(QString("%1").arg(FileName));
RCDsize+=Filesize;//更新读取的数据
if(!file->open(QFile::WriteOnly))
{
QMessageBox::information(this,"提示信息","无法读取文件");
return;
}
}
else
{
return;
}
}
if(RCDsize<Allsize)//如果接受的数据小于总数据,继续接受
{
RCDsize+=socket->bytesAvailable();
inblock=socket->readAll();
file->write(inblock);
inblock.resize(0);
}
//更新显示的数据
ui->lineEdit_4->setText(QString("%1").arg(RCDsize/(1024*1024))+"MB");
ui->lineEdit_5->setText(QString("%1").arg(Allsize/(1024*1024))+"MB");
if(RCDsize==Allsize)//如果接受的数据等于总数据,传输完成
{
file->close();
socket->close();
QMessageBox::information(this,"提示信息","接收完成",QMessageBox::Ok);
//将数据清空以便下次使用
file=nullptr;//文件指针
RCDsize=0;//接收的数据
ToBoSize=0;//未接收的数据
Filesize=0;//文件总大小
Allsize=0;//总大小
FileName="";//文件名称
inblock=0;//缓存
}
}
运行结果:
- 先运行服务器端,再运行客户端
- 客户端和服务器端都输入IP和端口号
- 服务器端选择文件
- 点击发送
1.输入主机和端口号
2.点击连接,然后点击监听
3.选择文件点击发送
数据太小所以0MB
注意:传输完会断开连接,所以还需要点击连接再点击监听
源码已发出:
QtTcp传输文件(简易)-C++文档类资源-CSDN文库