Qt多文件传输功能实现
- 前言
- 代码实现概述
- 客户端代码实现
- 服务端代码
- 效果展示
- 结论
前言
本次设计主要是为了功能上的实现,因此对于ui界面的设计都是怎么简单怎么来的,主要的功能就是实现多个文件的发送与接收,即客户端发送,服务端接收保存,并且删去客户端的已发送的文件。
代码实现概述
这里就不在概述tcp通信的相关的内容,主要围绕本文设计进行叙述:
第一就是文件发送的流程图,单次文件内容发送主要分为两次,首先是文件名,可用于服务端建立新的文件;其次是文件大小,可用服务端判断是否完全接收完;最后是文件地址,用于服务端删除客户端发送的文件,也可以把这认为是简单的协议:
第二就是多文件的发送:这里用到了QFileDialog类的getOpenFileNames函数(可以固定打开文件的类型),返回QStringList的文件路径集合,接下来就是遍历发送。
第三就是防止发生粘包的情况,使用QIODevice类waitForBytesWritten函数,每次等待数据内容发送。
客户端代码实现
tcpclient.h文件:
#ifndef TCPCLIENT_H
#define TCPCLIENT_H#include <QtWidgets/QMainWindow>
#include "ui_tcpclient.h"
#include <QHostAddress>
#include <QFile>
#include <QTcpSocket>class TcpClient : public QMainWindow
{Q_OBJECTpublic:TcpClient(QWidget *parent = 0);~TcpClient();void sendData();private:Ui::TcpClientClass ui;int port;QString ip;QTcpSocket *tcpSocket; //服务端连接套接字QFile locFile; //待发送的文件QList<QString> fileNames;QList<QString> paths;QList<qint64> fileSizes;qint64 sendSize;int sendTime;private slots:void slotSend(); //发送void slotOpen(); //打开文件void sendMessage(); //传输文件void sendData(int);
};#endif // TCPCLIENT_H
tcpclient.cpp文件:
#include "tcpclient.h"
#include <QFileDialog>
#include <QtDebug>
#include <QFileInfo>TcpClient::TcpClient(QWidget *parent): QMainWindow(parent)
{ui.setupUi(this);//初始化port = 8081;ip = "127.0.0.1";connect(ui.openButton,SIGNAL(clicked()),this,SLOT(slotOpen()));connect(ui.sendButton,SIGNAL(clicked()),this,SLOT(slotSend()));connect(ui.closeButton,SIGNAL(clicked()),this,SLOT(close()));ui.openButton->setEnabled(true);ui.sendButton->setEnabled(false);
}TcpClient::~TcpClient()
{}void TcpClient::slotOpen()
{//初始化数据fileNames.clear();fileSizes.clear();sendTime = 0;paths = QFileDialog::getOpenFileNames(this,"open file"); //当前目录下if (!paths.isEmpty()){for (int i = 0; i < paths.size(); i++){locFile.setFileName(paths[i]);if (locFile.open(QIODevice::ReadOnly)){//获取发送文件的信息QFileInfo info(paths[i]);fileNames.append(info.fileName()); //文件名fileSizes.append(info.size()); //文件大小}qDebug()<<"fileName:"<<fileNames[i]<<"fileSize:"<<fileSizes[i]<<"path:"<<paths[i];}}ui.sendButton->setEnabled(true);
}void TcpClient::slotSend()
{//创建连接服务器tcpSocket = new QTcpSocket(this);tcpSocket->connectToHost(ip,port); //连接服务器,并发出connected()信号connect(tcpSocket,SIGNAL(connected()),this,SLOT(sendMessage())); //传输文件槽函数
}void TcpClient::sendMessage()
{qDebug()<<"link successful";ui.sendButton->setEnabled(false);//先发送头,自定义组包, 文件名#文件大小,不是文件数据locFile.setFileName(paths[sendTime]);locFile.open(QIODevice::ReadOnly);QString head = QString("head#%1#%2#%3").arg(fileNames[sendTime]).arg(fileSizes[sendTime]).arg(paths[sendTime]);qDebug()<<"head:"<<head<<endl<<head.size();//发送头qint64 len = tcpSocket->write(head.toUtf8().data());tcpSocket->waitForBytesWritten(); //等待数据内容发送if (len > 0){sendData(sendTime); //发送内容数据}
}void TcpClient::sendData(int i)
{QByteArray buf = locFile.readAll();qint64 len = tcpSocket->write(buf);tcpSocket->waitForBytesWritten(); //等待数据内容发送sendSize = len;qDebug()<<"sendSize:"<<sendSize;if (sendSize == fileSizes[i]){qDebug()<<"send successful";locFile.close();tcpSocket->close();}if (i < paths.size()-1){slotSend();sendTime++;}
}
服务端代码
因为服务端的代码也比较简单,这里就不展示了。
效果展示
因为只是简单的功能设计,因此没有直观的展示,主要是客户端的发送截图,以及服务端对接收二进制文件内容的打印。
客户端界面:
服务端控制台打印文件内容:
结论
本次设计对单次文件传输是没什么问题的,主要的多文件传输时的粘包现象,本次设计经过验证对于多个小文件传输是没问题的,但是不排除大量的大文件不会出现问题。因此就需要一个更好更稳定的协议来保证,这次只是简单使用了等待传输的函数,后面我也会进一步进行更好的封包与解包的通信操作。见TCP解决粘包问题(结构数据封包拆包)