QT通过TCP协议传输文件
常用的网络协议分为TCP和UDP两种,两种方式各有优缺点,其中TCP的主要特点是可靠,但是速度较慢(不丢包);而UDP则是不可靠传输,但速度快(可能丢包)。今天主要介绍的就是通过QT框架和TCP协议实现简单的文件传输。
关键类
QT框架中和TCP协议相关的类主要有两个
QTcpServer 和QTcpSocket
其中QTcpServer
类用来在服务器端创建服务,而QTcpSocket
用来保持客户端与服务器之间的会话。
创建服务端
1、首先初始化服务端设置
//初始化server
QTcpServer *server = new QTcpServer(this);
//设置监听地址和端口(设置监听来自所有地址的 对5529端口的请求)
server->listen(QHostAddress::Any,55249);
2、设置接收到请求后创建和客户端会话的socket
//QTcpServer::newConnection在有新的连接时触发,通过槽函数MainWindow::initSocket初始化socket
connect(server,&QTcpServer::newConnection,this,&MainWindow::initSocket);
void MainWindow::initSocket()
{
//QTcpServer的nextPendingConnection()返回的是新连接的socket指针 (QTcpSocket* socket)
socket = server->nextPendingConnection();
//TcpSocket::readyRead在有新数据到来时触发一次,这里表示有数据需要接收了,通过槽函数ainWindow::writefile处理获取到的数据
connect(socket,&QTcpSocket::readyRead,this,&MainWindow::writefile);
}
3、获取到数据之后进行文件的读写
void MainWindow::writefile()
{
//通过readAll读取socket中的数据
QByteArray data = socket->readAll();
//isFile是一个bool型变量从来表示是否是文件数据,初始值为false,因为第一次发送的数据内容为文件信息,不是文件内容
if(!isFile){
//QString的section()用来分割字符,类似于python里的切片
filename = QString(data).section("#",1,1);
ui->label->setText(filename);
filesize = QString(data).section("#",2,2).toInt();
receiversize = 0;
file.setFileName(filename);
//设置文件打开模式
if(!file.open(QIODevice::WriteOnly))
{
QMessageBox::warning(this,tr("Warning"),tr("文件打开出错!"));
return;
}
ui->progressBar->setValue(0);
isFile = true;
}else
{
//写入文件数据
qint64 lenthR = file.write(data);
receiversize += lenthR;
ui->progressBar->setValue(receiversize*100/filesize);
}
if(receiversize == filesize)
{
QMessageBox::warning(this,tr("Warning"),tr("文件接收完成!"));
//关闭文件
file.close();
}
}
创建客户端
客户端较服务器端简单一些,客户端只需要创建QTcpSocket
的实例化对象连接服务器即可通信。
1、初始化客户端socket
//初始化客户端socket
socket = new QTcpSocket(this);
//连接服务器
socket->connectToHost("127.0.0.1",55249,QIODevice::ReadWrite);
//观察是否连接成功
if(socket->waitForConnected(1000))
{
QMessageBox::warning(this,tr("Warning"),tr("连接成功"));
}else{
QMessageBox::warning(this,tr("Warning"),tr("连接出错"));
}
2、选择需要传输的文件,初始化文件信息
//默认打开D盘,选择图片类型的文件
QString path = QFileDialog::getOpenFileName(this,tr("open File"),"d:/",tr("Images(*.png *.jpg *.xmp)"));
//初始化文件信息
if(!path.isEmpty())file.setFileName(path);
if(file.open(QIODevice::ReadOnly))
{
QFileInfo info(path);
filename = info.fileName();
filesize = info.size();
sendsize = 0;
QMessageBox::warning(this,tr("Warning"),tr("文件加载完成"));
}
3、发送自定义文件信息以及文件内容
//使用定时器连接发送数据方法
mytimer = new QTimer(this);
connect(mytimer,QTimer::timeout,this,&MainWindow::sendData);
//自定义文件信息
QString buf = QString("head#%1#%2").arg(filename).arg(filesize);
qint64 len = socket->write(buf.toUtf8().data());
//是否发送成功
if(len >0)
{
//使用定时器延时,防止粘包
mytimer->start(1000);
}else{
file.close();
}
void MainWindow::sendData()
{
qint64 len = 0;
do{
//每次发送2k文件内容
char buf[2*1024] = {0};
len = file.read(buf,sizeof(buf));
len = socket->write(buf,len);
sendsize+=len;
qDebug()<<"SendData";
}while(len >0);
if(sendsize == filesize)
{
QMessageBox::warning(this,tr("Warning"),tr("文件发送完毕"));
socket->close();
}
}
测试结果
本地测试可以进行图片的传输
本实例中还存在一些问题没有修复,如每次只能发送一张图片(发送完成后不能再选择图片发送等),还有很多,因为本文只进行文件传输的简单介绍,不多赘述。