Http多线程下载与断点续传分析.docx

上传人:b****7 文档编号:9767565 上传时间:2023-02-06 格式:DOCX 页数:9 大小:204.88KB
下载 相关 举报
Http多线程下载与断点续传分析.docx_第1页
第1页 / 共9页
Http多线程下载与断点续传分析.docx_第2页
第2页 / 共9页
Http多线程下载与断点续传分析.docx_第3页
第3页 / 共9页
Http多线程下载与断点续传分析.docx_第4页
第4页 / 共9页
Http多线程下载与断点续传分析.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

Http多线程下载与断点续传分析.docx

《Http多线程下载与断点续传分析.docx》由会员分享,可在线阅读,更多相关《Http多线程下载与断点续传分析.docx(9页珍藏版)》请在冰豆网上搜索。

Http多线程下载与断点续传分析.docx

Http多线程下载与断点续传分析

Http多线程下载与断点续传分析

   找了很多天的工作,真是找得有点郁闷,也就惰了下来!

发现现在的简历真是不值钱。

上次面试的时候本来投的是“高级程序员”职位,笔试也笔试,面试也面了。

本来还是信心满满的.不过后来在谈到薪水的时候,被贬到了初级程序员,给的高级程序员标准我还达不到,中级程序员的标准也需要一定条件--中级证书,郁闷的是我也没有!

最后被定位为初级程序员!

还真是有点打击人。

   不过我到现在也不知道初中高级程序员的标准究竟在哪里,平时总是尽量让自己提高,也很少去考滤到证书的重要性!

总之最后就是泡汤了

   好了,口水就吐到这里吧,转入正题!

投简历归投简历,剩余时间总是喜欢做一点自己喜欢做的事。

   有时候发现最快乐的事就是静静的听着音乐,敲着代码,写一些东西与朋友一起分享,一起研究。

   上次的-“Mp3在线搜索工具”还有很多可以改进的地方,也得到一些朋友的建议,非常感谢。

这个版本中加入了断点续传的功能,使用了XML文件保存任务列表及状态信息,并且支持多线程分段下载,提高下载速度,在这一个版本中,我把它叫做:

JLoading因为我还想不出一个更好听一点或更酷一些的名字,

而且我还想让他可以下载一些其它文件。

程序不想做大,但想做得极致一些,比如从速度上。

欢迎交流学习,huliqing(huliqing@)

Jloading完整程序下载

Jloading源码下载(仅供学习研究使用,有任何问题请与本人联系)

协议针对于Http,先谈一下简单原理。

因为代码太多,在这里只取重点分析。

   如果你对http协议比较了解,那么你应该已经知道原理了,只要在请求头中加入以下代码就可以只请求部分数据:

Content-Range:

bytes20000-40000/47000,

即从第20000字节请求到第40000个字节,(文件长度是47000字节).知道了这一点之后,请求数据就非常容易了,

只要利用Java中的URL,先探测数据的长度,然后再将这个长度分片段进行多段程下载就可以了,以下是我的实现方式:

      // 对文件进行分块

        try {

            totalBytes = new URL(url).openConnection().getContentLength();

            if (totalBytes == -1) state = STATE_NONE;

        } catch (IOException ioe) {

            return;

        }

        // 创建分块,并创建相应的负责下载的线程

        long pieceSize = (long) Math.ceil((double) totalBytes / (double) threads);

        long pStart = 0;

        long pEnd = 0;

        tasksAll.clear();

        tasks.clear();

        for (int i = 0; i < threads; i++) {

            if (i == 0) {

                pStart = pieceSize * i;

            }

            if (i == threads - 1) {

                pEnd = totalBytes;

            } else {

                pEnd = pStart + pieceSize;

            }

            Piece piece = new Piece(pStart, pStart, pEnd);

            tasksAll.add(piece);

            tasks.add(piece);

            pStart = pEnd + 1;

        }

 程序根据线程数目划分成相应的分片信息(Piece)并放入任务列表中,由线程池中的线程去负责下载,每个线程负责一个片段(Piece),当线程下载完自己的分片之后并不立即消毁,而是到任务队列中继续获取任务,

若任务池为空,则将自己放入空闲池中并等待新任务,其他线程在发现有空闲线程时,则将自己所负责的任务分片再进行切割,并放入到任务队列中,同时唤醒空闲线程帮助下载,这样不会出现懒惰线程,也可以实现动态增删线程的功能,注意的是一些线程同步的问题。

public void run() {

        while (!

dl.isOk()) {

            

            // 暂停任务

            synchronized (this) {

                if (dl.isPaused()) {

                    try {

                        this.wait();

                    } catch (InterruptedException e) {

                    }

                }

            }

            

            // 中断停止

            if (Thread.interrupted() || dl.isStopped()) {

                return;

            }

            

            // 等待获取任务

            Piece piece;

            synchronized (tasks) {

                while (tasks.isEmpty()) {

                    if (dl.isOk()) return;

                    try {

                        tasks.wait();

                        //System.out.println(this.getName() + ":

wait

");

                    } catch (InterruptedException ie) {

                        //System.out.println(this.getName() + 

                        //        ":

InterruptedException:

" + ie.getMessage());

                    }

                }

                piece = tasks.remove(0);

                dl.removeFreeLoader(this);

                //System.out.println(this.getName() + ":

loading

");

            }

            try {

                URL u = new URL(dl.getURL());

                URLConnection uc = u.openConnection();

                // 设置断点续传位置

                uc.setAllowUserInteraction(true);

                uc.setRequestProperty("Range", "bytes=" + piece.getPos() + "-" + piece.getEnd());

                in = new BufferedInputStream(uc.getInputStream());

                out = new RandomAccessFile(dl.getFileProcess(), "rw");

                out.seek(piece.getPos()); // 设置指针位置

                long start;

                long end;

                int len = 0;

                while (piece.getPos() < piece.getEnd()) {

                    start = System.currentTimeMillis();

                    len = in.read(buff, 0, buff.length);

                    if (len == -1) break;

                    out.write(buff, 0, len);

                    end = System.currentTimeMillis();

                    timeUsed += end - start;    // 累计时间使用

                    

                    long newPos = piece.getPos() + len;

                    

                    // 如果该区段已经完成,如果该线程负责的区域已经完成,或出界

                    if (newPos > piece.getEnd()) {

                        piece.setPos(piece.getEnd());   

                        long offset = newPos - piece.getEnd();

                        long trueReads = (len - offset + 1);

                        dl.growReadBytes(trueReads);    // 修正偏移量

                        dl.setOffsetTotal(dl.getOffsetTotal() + trueReads);

                        readBytes += trueReads;

                        //System.out.println(this.getName() + ":

read=" + trueReads);

                    } else {

                        dl.growReadBytes(len);

                        piece.setPos(piece.getPos() + len);

                        readBytes += len;

                        //System.out.println(this.getName() + ":

read=" + len);

                    }

                    // 如果存在空闲的任务线程,则切割出新的区域至任务队列中。

由空闲

                    // 的线程辅助下载

                    if (dl.isFreeLoader()) {

                        Piece newPiece = piece.cutPiece();

                        if (newPiece !

= null) {

                            synchronized (tasks) {

                                dl.addTask(newPiece);

                                dl.setRepairCount(dl.getRepairCount() + 1); // 增加切割次数

                                tasks.notifyAll();  // 唤醒等待任务中的空闲线程

                            }

                        }

                        

                    }

                    // 暂停任务

                    synchronized (this) {

                        if (dl.isPaused()) {

                            try {

                                this.wait();

                            } catch (InterruptedException e) {

                            }

                        }

                    }

                    

                    // 中断停止

                    if (Thread.interrupted() || dl.isStopped()) {

                        in.close();

                        out.close();

                        return;

                    }

                    //System.out.println(this.getName() + ":

read:

" + dl.getReadBytes());

                }

                out.close();

                in.close(); 

                dl.addFreeLoader(this);

                //System.out.println("切割次数:

" + dl.getRepairCount());

                if (dl.isOk()) dl.processWhenOk();

            } catch (IOException e) {

                //System.out.println(this.getName() + ":

无法读取数据");

            }

        }

    }

这里使用了RandomAccessFile进行保存本地文件,使用RandomAccessFile可以快速移动指针。

另外一个就是需要在运行过程中,记得定时保存分片信息,以免在程序意外崩溃的情况下无法正常保存状态信息。

程序可能还存在一些不足并且还有很多可以改进的地方。

以下是状态文件Config的XML结构,这里记录了每个任务的运行状态,piece作为每个分段的状态,在程序重启之后载入这个配置文件,并在下载完成之后删除这一文件.另外主目录还有一个task.xml文件,用于记录所有下载任务,及状态文件的位置。

xml version="1.0" encoding="UTF-8" standalone="no"?

>

    length="3895507" 

    name="读你  36首经典精选" 

    save="C:

\Documents and Settings\huliqing.TBUY-HULIQING\桌面\dist\musics\读你  36首经典精选[1].mp3" 

    threads="12">

    

    

    

    

    

    

    

    

    

    

    

    

    

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1