一种实现简单网络断点续传的方法介绍

30 篇文章 4 订阅
12 篇文章 3 订阅
该博客详细介绍了如何实现简单的网络断点续传功能。主要思路包括发送方找到断点并发送文件信息,接收方获取信息并判断是否为续传。重难点在于文件信息的预发送、全局变量记录发送与接收进度以及从断点开始的发送与接收。实现方式中,发送方通过QFile的seek()函数定位断点,接收方根据接收到的信息判断文件状态并以追加方式写入文件。
摘要由CSDN通过智能技术生成

一、思路分析

断点续传的思路其实也简单。

  1. 实现发送方的数据接续发送。
  2. 实现数据接收方的数据断点接收。

二、重难点分析

为了实现这两点,思路也很简单,关键点在于:

  1. 在文件发送之前,应该先发送一个先行报文,告诉数据接收方文件名,文件大小,中断前已发送的文件包数。
  2. 需要有一个全局变量,记录下每一个发送文件的发送进度,以便在一次发送中断时,在下次能找到断点。
  3. 实现从中断处开始发送数据。
  4. 判断文件是否是新接收还是之前已接受过。新接收则创建再接收,已接受过则判断是否已接收完毕,避免重复接收。
  5. 需要有一个全局变量,记录下每一个文件的接收进度,以便在一次接收中断时,能保存下来断点。
  6. 实现从中断处开始接收并写入数据。
  7. 在断点处,对已发送数据和已接收数据的大小进行比较,确保之前发送的已经确定接收,避免数据丢失。
  8. 使用追加写入的方式写入文件,避免覆写。

三、实现方式

为解决上述的重难点,提出以下解决思路。

(一)发送方

1 找到并指向断点位置

创建一个全局函数,记录发送进度,每次启动发送时,利用QFile的seek()函数移动指针到已发送的位置。
头文件全局函数

		qint64 Process = 0;

cpp文件

         //打开文件
         QFile SrcFile(send_file);
         if(!SrcFile.open(QIODevice::ReadOnly))
         {
             LogSend(QString("%1 文件打开失败.停止发送.\n").arg(send_file));
             return;
         }

         //得到文件大小信息
         File_TotalBytes=SrcFile.size();
         Send_TotalBytes = iter.value().Process;
         //指针指向断点,如为新发送文件,指向0
         SrcFile.seek(Process);
2 先行发送文件信息报文
         QString str;
         QFileInfo send_file_info(send_file);
         //提前发送的的一个报文,告诉数据接收端将要发送的文件的文件名和文件大小,以及已发送的文件大小(因为可能是断点续传)。
         str=QString("#%1#%2#%3").arg(send_file_info.fileName()).arg(send_file_info.size()).arg(Send_TotalBytes);
         LogSend(str);

         qDebug()<<"str.toStdString().c_str():"<<str.toStdString().c_str()<<",len:"<<str.size();

         if (UDT::ERROR == (ss = UDT::send(client, str.toStdString().c_str(),strlen(str.toStdString().c_str()), 0)))
         {
             LogSend("send_error:" + QString(UDT::getlasterror().getErrorMessage()));
             return;
         }
3 发送并记录断点
             if (UDT::ERROR == (send_len = UDT::send(client,byte.data(),byte.size(), 0)))
             {
                 LogSend("send_error:" + QString(UDT::getlasterror().getErrorMessage()));
                 break;
             }
              //记录发送的长度
              Process+=send_len;

(二)接收方

1 获取发送文件的信息并判断

拆解第一包,获取将接收的文件的信息,文件名,大小,已接收的大小,已经判断是否为续传,新接收则创建,不是则判断是否之前已经接收完毕,是否需要续传,如果需要续传,则从全局函数获取断点信息,并判断已接收文件数据和发送者已发送数据是否一致。

	qint64 s_file_size=0; //文件大小
    qint64 s_already_recv_size=0; //文件已接收大小,续传
    QString s_file_name;//包含路径
    QString fileName;//仅文件名
    std::string std_str_data=(char*)data;
    QString Rx_str_data=QString("%1").fromStdString(std_str_data);
    LogSend("接收的原始第一包数据:"+Rx_str_data);
    int addr=Rx_str_data.indexOf(QByteArray("#"));
    if(addr!=-1) //查找成功
    {
        QStringList Strs = Rx_str_data.split("#");
        fileName = Strs.at(0);//取出文件名
        QString str_size=Strs.at(1); //取出文件字节大小字符串
        QString str_already_recv_size = Strs.at(2);
        s_file_name=QString("%1/%2").arg(recv_save_path).arg(fileName);
        s_file_size=str_size.toLongLong();   //得到文件字节大小
        s_already_recv_size = str_already_recv_size.toLongLong(); //得到之前已经传送的文件大小
        //判断文件是否已经存在
        recv_file_p=new QFile(s_file_name);
        if(recv_file_p->exists())
        {
            //如果原来存在,则打开
            if(recv_file_p->open(QIODevice::ReadWrite))
            {
                //如果已经接收完毕,或者已经接收的文件大小和已经发送的文件大小不一致,则退出
                if(recv_file_p->size()==s_file_size || s_already_recv_size!=Map_recv_fInfos[fileName].Process)
                {
                    //大小相同,则认为已经接收完毕,否则认为是续传。
                    recv_file_p->close();
                    delete recv_file_p;
                    recv_file_p=nullptr;

                    if(recv_file_p->size()==s_file_size)
                    {
                        LogSend(QString("线程退出:%1:%2:%3。因为文件已经接收完毕\n").arg(s_file_name).arg(s_file_size).arg(file_len));
                    }else if(s_already_recv_size!=Map_recv_fInfos[fileName].Process){
                        LogSend(QString("线程退出:%1:%2:%3。已接收文件和已发送文件大小不一致,续传失败!请删除已接收文件,然后重传!\n").arg(s_file_name).arg(s_file_size).arg(file_len));
                    }

                    emit recv_stop_signal();

                    delete [] data;

                    UDT::close(recver);

                    #ifndef WIN32
                       return NULL;
                    #else
                       return 0;
                    #endif
                }
                //设置断点
                file_len = Map_recv_fInfos[fileName].Process;
                LogSend(QString("%1 文件打开成功,准备续传.\n").arg(s_file_name));
            }
            else
            {
                recv_file_p=nullptr;
                LogSend(QString("%1 文件打开失败.\n").arg(s_file_name));
            }

        }else{
            if(recv_file_p->open(QIODevice::ReadWrite))
            {
                fInfos newfInfos;
                newfInfos.filePath = recv_save_path;
                newfInfos.size = s_file_size;
                newfInfos.Process = 0;
                newfInfos.isRecvOk = false;
                newfInfos.speed=0.0;
                Map_recv_fInfos.insert(fileName,newfInfos);

                LogSend(QString("%1 文件创建成功.\n").arg(s_file_name));
            }
            else
            {
                recv_file_p=nullptr;
                LogSend(QString("%1 文件创建失败.\n").arg(s_file_name));
            }
        }
2 记录接收文件的断点

边接收边记录已接收文件内容的大小。

       if (UDT::ERROR == (rs = UDT::recv(recver,(char*)data,size,0)))
       {
          LogSend("recv:" +QString(UDT::getlasterror().getErrorMessage()));
          LogSend("客户端断开连接");
          break;
       }
       file_len+=rs;
       Process=file_len;
3 追加的方式写入文件
           QTextStream out(recv_file_p);		//输入流
           out<<(char*)data;			//写入内容
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼月半

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值