应用需求:

网盘开发工作逐步进入各部分的整合阶段,当用户在client改动或新添加一个文件时。该文件要同步上传到server端相应的用户文件夹下,因此针对传输数据(即:上传、下载)这一块如今既定了三种传输方式,即:Ftp传输、HTTP传输以及基于UDT的传输。

且这三种传输数据方式是可配的,能够通过不同的接口调用。相比这三种方式。基于UDT的大量文件传输是比較值得研究与创新的地方,它在底层是基于UDP,在上层实现了可靠性的控制;同一时候它充分考虑到了基于在公网环境下基于Tcp进行传输时拥塞控制算法的缺点,实现了自己的拥塞控制算法,在实际測试中其性能也是明显高于基于Tcp的传输。

关于UDT实现文件传输仅仅进行了技术调研,还没有真正实现。这一部分内容将在兴许文章中提及。这三天的时间仅仅实现了基于FTP的支持断点续传的文件上传、下载。

实现原理:

离我们近期的断点续传的应用样例是:迅雷。当使用迅雷下载一个大文件时,它实现了以下的功能:1> 电脑突然断电或程序突然退出后,当我们又一次启动迅雷时它还会从程序退出时已经下载的文件点继续向后下载,而不是文件又从头開始下载。2> 能够设置採用多个线程同一时候下载,每一个线程仅仅下载文件里的某一部分,比如:使用三个线程下载一个9000个字节的文件。则第一个线程下载第1—3000个字节,第二个线程下载第3001—6000个字节,第三个线程下载第6001—9000个字节。这三个线程是同一时候下载一个文件,仅仅是下载不同的部分。它会把下载的文件片段暂存在某个位置,当三个线程所有下载完毕时再拼成一个完整的文件。

这里不用多说,其长处显而易见。

事实上,断点续传实现的原理非常easy,就是不管是上传还是下载时都能够实时记录下已经上传了或下载了多少字节,假设中间由于某种原因传输断开,下载启动时仅仅须要再又一次从已经下载的位置继续下载或上传就能够了。

利用Qftp实现断点续传:

QT中有一个实现Ftp的类:Qftp,它提供了主要的ftp的使用方式,连接ftpserver:connectToHost;登录:login。上传:put;下载:get。使用这些方法能够实现与ftpserver交互实现文件上传、下载。

可是使用它原生提供的put与get方法,无法实现断点续传。因此。为了实现断点续传我们须要又一次实现文件传输。并在当中加入断点续传的控制。事实上Ftp文件传输的本质也是使用Tcp来实现底层的文件传输。大体思路就是:利用Qftp的connectToHost登录ftpserver,使用login登录ftpserver,使用rawCommand发送ftp原生态的命令,使用QTcpSocket实现文件数据的传输。

首先,使用QTcpSocket实现文件数据的传输,须要设置ftpserver为“PASV”被动接收方式。即ftpserver被动地接收来自client的连接请求。

Ftpserver全部能够发送的原生命令有:http://www.nsftools.com/tips/RawFTP.htm。

实现断点上传的命令发送流程:

1、rawCommand("TYPE I");设置数据传输的类型:二进制数据或ASCII

2、rawCommand("PASV")。设置server为被动接收方式。发送PASV命令后。server会返回自己开启的传输数据的port。等待client连接进行传输数据。

返回的数据格式为:“227 Entering Passive Mode (192, 168, 2, 18, 118, 32)”,然后从返回的信息里面或去相关的信息,ftpserver的IP地址:192.168.2.18;ftpserver开启的用于传输数据的port:118*256 + 32 = 30240。获得该信息后就须要建立TcpSocket的通信链路。连接ftpserver。

3、rawCommand("APPE  remote-file-path");设置server端remote-file-path为追加的方式。

假设此时改文件不存在,则server端会创建一个。

4、完毕上述流程后,就能够打开本地文件进行读取,并通过tcpsocket链路发送出去(write)。

实现断点下载的命令发送流程:

1、rawCommand("TYPE I");设置数据传输的类型:二进制数据或ASCII

2、rawCommand("PASV");设置server为被动接收方式。发送PASV命令后,server会返回自己开启的传输数据的port,等待client连接进行传输数据。

返回的数据格式为:“227 Entering Passive Mode (192, 168, 2, 18, 118, 32)”。然后从返回的信息里面或去相关的信息,ftpserver的IP地址:192.168.2.18;ftpserver开启的用于传输数据的port:118*256 + 32 = 30240;获得该信息后就须要建立TcpSocket的通信链路。连接ftpserver。

3、rawCommand("REST  size");该命令设置ftpserver从本地文件的哪个地方開始进行传输数据。

4、rawCommand(“RETR  remote-file-path”);開始从远程主机传输文件。

文件上传时在设置APPE返回之后,就能够打开本地文件进行上传;文件下载时,收到PASV的返回信息建立tcpsocket的连接后,须要建立readyRead()的信号槽,在该槽函数中实现数据的读取。

关键代码:

1. 处理rawCommand()发送原生命令返回后的槽函数:

void LHTFileTransfer::ProcRawCommandReply(int nReplyCode, QString strDetail)
{//! TYPEif (200 == nReplyCode){m_ftpHandle->rawCommand("PASV");if (currentItem.task_type.compare("Upload") == 0){op = QString("Put");}else if (currentItem.task_type.compare("DownLoad") == 0){op = QString("Get");}}//! PASVelse if(227 == nReplyCode){const QString backResult = strDetail;if (NULL != m_sendDataSocket){m_sendDataSocket->close();delete m_sendDataSocket;}m_sendDataSocket = new QTcpSocket();connect(m_sendDataSocket, SIGNAL(readyRead()), this, SLOT(ProcReadyRead()), Qt::UniqueConnection);connect(m_sendDataSocket, SIGNAL(readChannelFinished()), this, SLOT(ProcReadChannelFinished()), Qt::UniqueConnection);connect(m_sendDataSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(ProcBytesWritten(qint64)), Qt::UniqueConnection);QStringList lstr = backResult.split("(").last().split(")").first().split(",");int nAddress = lstr.at(0).toInt()<<24 |lstr.at(1).toInt()<<16 |lstr.at(2).toInt()<<8 |lstr.at(3).toInt();QHostAddress hostAddress(nAddress);int nPort = lstr.at(lstr.length() - 2).toInt() * 256 + lstr.last().toInt();m_sendDataSocket->connectToHost(hostAddress, nPort);//! APPE , 须要接远程文件的绝对路径QString appeShell;if (op.compare("Put") == 0){appeShell = QString("APPE %1").arg(currentItem.file_remote_path);}else if (op.compare("Get") == 0){//! 这里的REST后面的大小应该为本地保存的问价的大小appeShell = QString("REST 0");}        m_ftpHandle->rawCommand(appeShell);}//! 发送数据else if (150 == nReplyCode){if (op.compare("Put") == 0){m_fileHandle = new QFile(currentItemFilePath);if (!m_fileHandle->open(QIODevice::ReadOnly)){qDebug() << "file open error ...";return ;}const qint64 fileSize = m_fileHandle->size();m_fileHandle->seek(currentItem.uploaded_size);while(!m_fileHandle->atEnd()){const qint64 nBlockSize = 16 * 1024 ;char buf[16 * 1024];qint64 nowPos = m_fileHandle->pos();qint64 readLen = m_fileHandle->read(buf, nBlockSize);if (readLen !=0 && readLen != -1){m_sendDataSocket->write(buf, readLen);m_sendDataSocket->flush();emit DataTransferProgress(nowPos, fileSize);}}m_sendDataSocket->flush();m_sendDataSocket->close();m_sendDataSocket = NULL ;emit DataTransferProgress(m_fileHandle->pos(), m_fileHandle->size());m_procTask.remove(currentItemFilePath);m_fileHandle->close();//emit StartNextTask();}else if(op.compare("Get") == 0){m_fileHandle = new QFile(currentItem.file_remote_path);if (!m_fileHandle->open(QIODevice::WriteOnly)){qDebug() << "file open error ...";return ;}}}else if(350 == nReplyCode){QString shell = QString("RETR %1").arg(currentItemFilePath);m_ftpHandle->rawCommand(shell);}
}

2. 断点下载时实现buffer读取的函数:

void LHTFileTransfer::ProcReadyRead()
{qDebug() << "[DownLoad] ProcReadyReady ....";QByteArray buffer = m_sendDataSocket->readAll();m_fileHandle->write(buffer);   m_fileHandle->flush();emit DataTransferProgress(m_fileHandle->size(), 0);
}

面临的问题以及兴许须要优化的地方:

1. 字符编码问题。即当须要上传的文件名称是中文名称时,须要对其进行转码。

2. 如今实现的是单线程,尚未加入多线程断点下载以及队列的实现。

转载于:https://www.cnblogs.com/yangykaifa/p/6994569.html

【大话QT之十】实现FTP断点续传相关推荐

  1. 【大话QT之十四】QT实现多语言切换

    功能需求: 网盘客户端要能够实现多国语言的切换,第一版要支持中.英文的切换.在实现过程中感觉QT对多国语言的支持还是很不错的,制作多语言包很方便,切换的逻辑也很简单.下面就来看一下QT中如何制作多语言 ...

  2. ftp 断点续传 Android,Android使用FTP实现断点续传

    Android使用FTP实现断点续传 断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传 ...

  3. 【大话QT之四】ctkPlugin插件系统实现项目插件式开发

    插件式开发体会: 自开始写[大话QT]系列就开始接触渲染客户端的开发,说是开发不如更多的说是维护以及重构,在接手这块的东西之前自己还有点犹豫,因为之前我一直认为客户端嘛,没什么技术含量,总是想做比较有 ...

  4. 马哥教育第二十四ftp协议、vsftpd的高级应用、rpc概念及nfs的基本应用、samba及其基本应用...

    1.ftp协议及vsftpd的基本应用          文件共享服务:                  工作在应用层:ftp(file transfer protocol)             ...

  5. linux c ftp断点续传,求个支持断点续传的ftp脚本

    RT,脚本能实现断点续传么?该怎么写呢,搜索了下论坛未果,希望大家帮忙.PS:在UNIX下跑的 | ftp  可以 , put , restart , put 下面測試 ftp> put lin ...

  6. java ftp 断点,java实现ftp断点续传

    //import cz.dhl.io.*; //import cz.dhl.ftp.*; import .ftp.*; import .*; import java.applet.*; import ...

  7. Hello Qt(十)——QT输入组件

    一.QComboBox下拉列表框 1.QComboBox组件简介 QComboBox下拉框继承自QWidget,用于有多个选项的下拉框. 2.QComboBox组件属性 QComboBox组件属性设置 ...

  8. Linux学习之CentOS(三十六)--FTP服务原理及vsfptd的安装、配置

    本篇随笔将讲解FTP服务的原理以及vsfptd这个最常用的FTP服务程序的安装与配置... 一.FTP服务原理 FTP(File Transfer Protocol)是一个非常古老并且应用十分广泛的文 ...

  9. 大话设计模式(十四 设计模式不能戏说!设计模式怎就不能戏说?)

    (续上篇) 次日,小菜来到大鸟处.        "大鸟,你在写什么东西?"小菜看到大鸟的电脑上开着记事本.        "哦,我打算写篇博客,名字就叫<设计模式 ...

最新文章

  1. Dubbo的总体架构
  2. UWA官方Demo新增《小米超神》,全面揭秘重度手游的性能表现!
  3. Android TabWidget
  4. electron创建菜单
  5. Linux 开机显示:welcome to emergency mode
  6. 织梦熊掌号插件兼容php5.3,DedeCMS百度熊掌号推送插件下载
  7. tsl加密算法_HTTPS背后的加密算法(转)
  8. Elasticsearch:Elasticsearch基础上构建推荐引擎 资料收集
  9. 网络请求之优化参数添加工具类自定义Map类
  10. vscode生成vue模板快捷键_vs code 快速生成vue 模板
  11. django分页-Paginator类
  12. Hadoop环境搭建 MYSQL环境配置
  13. 使用云开发实现微信支付的具体方法
  14. 图灵接口 php,图灵机器人API接口
  15. cmd文件和bat文件的区别+一个的bat脚本+bat基础知识
  16. Win10喇叭图标出现红叉,没有声音,并且提示“未安装任何音频输出设备“的解决办法
  17. 【随手写】JS过滤所有script正则
  18. 多点生活的分布式服务框架DSF
  19. Jetson Xavier NX学习笔记(三)系统烧录+开机教程+YOLOv7环境搭建+错误总结(详细版)
  20. 下载正版的Windows操作系统和office软件

热门文章

  1. 冈萨雷斯数字图像处理(本科教学版)第三章课本习题全解matlab
  2. Java基础知识-char
  3. Layui-颜色选择器
  4. C#小游戏--飞行棋
  5. 架构设计参考项目系列主题:最全的权限系统设计方案
  6. cobbler批量装机系统centos 6.4下安装配置
  7. jsp include jsp 中文乱码问题
  8. 计算机考研复试——操作系统篇
  9. Oracle常用函数汇总记录
  10. Householder变换、Givens旋转与QR分解