zhouyiran2
最近在写资源下载的时候讨巧想加快速度使用了多线程,但是发现结果是下载十分容易出现服务器请求处理缓慢、数据请求缓慢等多种问题,不知道是不是个例,大家自己写的时候可以注意一下

PS:资源下载指"assets"目录内的那些文件
PS2:个人猜测是服务器进行了限制防止服务器卡爆
PS3:也极有可能是本人代码质量问题(属于脚踩西瓜皮,写到哪里是哪里的那种)原因

LittleHei
C++读不下去了吧骚年。。。。。。

zhouyiran2
987987 发表于 2014-1-2 21:26
C++读不下去了吧骚年。。。。。。

哦,那个忘更新了,早就看完了。。。只是目前在用C#罢了

leo890817
Assets資源包蠻重要的,唱片會以.ogg的形式存在,這對駭檔很有用阿!

LittleHei
zhouyiran2 发表于 2014-1-2 21:27
哦,那个忘更新了,早就看完了。。。只是目前在用C#罢了

话说以前见到你的时候我只是4级,最近没水吗骚年=. =

zhouyiran2
987987 发表于 2014-1-2 21:29
话说以前见到你的时候我只是4级,最近没水吗骚年=. =

对,最近好久没来论坛了。。。

bangbang93
BMCL表示多线程未碰到任何障碍

52Dora
也要注意的是:
MCP+Forge安装时要开代理(例如t*e*n*a*c*y),否则下载assets死活出错然后安装失败

Sorta
笑喷了 怎么可能,,

zhouyiran2
Sorta 发表于 2014-1-4 14:12
笑喷了 怎么可能,,

你试过吗?

Sorta
zhouyiran2 发表于 2014-1-4 16:18
你试过吗?

...不可能的 单线程的话 慢的跟猪一样 没有任何开发者会在如此重要的一个部分出错

zhouyiran2
Sorta 发表于 2014-1-4 16:21
...不可能的 单线程的话 慢的跟猪一样 没有任何开发者会在如此重要的一个部分出错

1.你确定单线程慢得像猪?请搞搞清楚这些概念
2.这不是一个错误,这可能是应对策略,MC有1300W正版用户,就算其中有很多已经不玩了,但是你也要考虑到盗版的人数。当版本更新时,如此多的客户端会在比较短的时间内各自发送成百个请求获取资源文件,你去查查吧,已经有过数据流量过大被Amazon限流的先例了(曾今MC资源服务器是被挂在Amazon AWS 上的)现在具体不是很清楚

Sorta
zhouyiran2 发表于 2014-1-4 16:30
1.你确定单线程慢得像猪?请搞搞清楚这些概念
2.这不是一个错误,这可能是应对策略,MC有1300W正版用户, ...

1.同源下载多线程还是比单线程快。。
2.请用逻辑的眼光看待编程

zhouyiran2
Sorta 发表于 2014-1-4 17:28
1.同源下载多线程还是比单线程快。。
2.请用逻辑的眼光看待编程

1.请记住,建立连接是需要时间的,所有多线程的优势在于较大一些的文件上会有一定优势,但在assets资源中,有大量小文件,这个请自己比较
2.官方和几乎所有第三方启动器也都是使用的是单线程下载,具体是巧合大家都脑残了还是有什么原因这个我也不知道
3.这个结论是我亲身经历得出的,想要反驳你可以自己先试一试,试过再说大家也更服你,不是吗?

Sorta
zhouyiran2 发表于 2014-1-5 16:27
1.请记住,建立连接是需要时间的,所有多线程的优势在于较大一些的文件上会有一定优势,但在assets资源中 ...

第一种多线程同源多线程

  1.     import java.io.File;   
  2.     import java.io.IOException;   
  3.     import java.io.InputStream;   
  4.     import java.io.RandomAccessFile;   
  5.     import java.net.HttpURLConnection;   
  6.     import java.net.URL;   
  7.     /**  
  8.      * 多线程方式文件下载  
  9.      */   
  10.     public class MulThreadDownload {   
  11.         /* 下载的URL */   
  12.         private URL downloadUrl;   
  13.         /* 用于保存的本地文件 */   
  14.         private File localFile;   
  15.         /* 没条线程下载的数据长度 */   
  16.         private int block;   
  17.         public static void main(String[] args) {   
  18.             /* 可以为网络上任意合法下载地址 */   
  19.             String downPath = "http://192.168.1.102:8080/myvideoweb/down.avi";   
  20.             MulThreadDownload threadDownload = new MulThreadDownload();   
  21.             /* 开 10 条线程下载下载 */   
  22.             try {   
  23.                 threadDownload.download(downPath, 10);   
  24.             } catch (Exception e) {   
  25.                 e.printStackTrace();   
  26.             }   
  27.         }   
  28.         /**  
  29.          * 多线程文件下载  
  30.          *   
  31.          * @param path 下载地址  
  32.          * @param threadCount 线程数  
  33.          */   
  34.         public void download(String path, int threadCount) throws Exception {   
  35.             downloadUrl = new URL(path);   
  36.             HttpURLConnection conn = (HttpURLConnection) downloadUrl   
  37.                     .openConnection();   
  38.             /* 设置 GET 请求方式 */   
  39.             conn.setRequestMethod("GET");   
  40.             /* 设置响应时间超时为 5 秒 */   
  41.             conn.setConnectTimeout(5 * 1000);   
  42.             /* 获取本地文件名 */   
  43.             String filename = parseFilename(path);   
  44.             /* 获取下载文件的总大小 */   
  45.             int dataLen = conn.getContentLength();   
  46.             if (dataLen < 0) {   
  47.                 System.out.println("获取数据失败");   
  48.                 return;   
  49.             }   
  50.             /* 创建本地目标文件,并设置其大小为准备下载文件的总大小 */   
  51.             localFile = new File(filename);   
  52.             RandomAccessFile accessFile = new RandomAccessFile(localFile, "rwd");   
  53.             /* 这时候,其实本地目录下,已经创建好了一个大小为下载文件的总大小的文件 */   
  54.             accessFile.setLength(dataLen);   
  55.             accessFile.close();   
  56.             /* 计算每条线程要下载的数据大小 */   
  57.             block = dataLen % threadCount == 0 ? dataLen / threadCount : dataLen / threadCount + 1;   
  58.             /* 启动线程下载文件 */   
  59.             for (int i = 0; i < threadCount; i++) {   
  60.                 new DownloadThread(i).start();   
  61.             }   
  62.         }   
  63.         /**  
  64.          * 解析文件  
  65.          */   
  66.         private String parseFilename(String path) {   
  67.             return path.substring(path.lastIndexOf("/") + 1);   
  68.         }   
  69.         /**  
  70.          * 内部类: 文件下载线程类  
  71.          */   
  72.         private final class DownloadThread extends Thread {   
  73.             /* 线程 id */   
  74.             private int threadid;   
  75.             /* 开始下载的位置 */   
  76.             private int startPosition;   
  77.             /* 结束下载的位置 */   
  78.             private int endPosition;   
  79.             /**  
  80.              * 新建一个下载线程  
  81.              * @param threadid 线程 id  
  82.              */   
  83.             public DownloadThread(int threadid) {   
  84.                 this.threadid = threadid;   
  85.                 startPosition = threadid * block;   
  86.                 endPosition = (threadid + 1) * block - 1;   
  87.             }   
  88.             @Override   
  89.             public void run() {   
  90.                 System.out.println("线程 '" + threadid + "'启动下载..");   
  91.                     
  92.                 RandomAccessFile accessFile = null;   
  93.                 try {   
  94.                     /* 设置从本地文件的什么位置开始写入数据 ,"rwd" 表示对文件具有读写删权限 */   
  95.                     accessFile = new RandomAccessFile(localFile, "rwd");   
  96.                     accessFile.seek(startPosition);   
  97.                     HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();   
  98.                     conn.setRequestMethod("GET");   
  99.                     conn.setReadTimeout(5 * 1000);   
  100.                     /* 为 HTTP 设置 Range 属性,可以指定服务器返回数据的范围 */   
  101.                     conn.setRequestProperty("Range", "bytes=" + startPosition + "-"   
  102.                             + endPosition);   
  103.                     /* 将数据写往本地文件 */   
  104.                     writeTo(accessFile, conn);   
  105.                         
  106.                     System.out.println("线程 '" + threadid + "'完成下载");   
  107.                 } catch (IOException e) {   
  108.                     e.printStackTrace();   
  109.                 } finally {   
  110.                     try {   
  111.                         if(accessFile != null) {   
  112.                             accessFile.close();   
  113.                         }   
  114.                      } catch (IOException ex) {   
  115.                          ex.printStackTrace();   
  116.                      }   
  117.                 }   
  118.             }   
  119.             /**  
  120.              * 将下载数据写往本地文件  
  121.              */   
  122.             private void writeTo(RandomAccessFile accessFile,   
  123.                     HttpURLConnection conn){   
  124.                 InputStream is = null;   
  125.                 try {   
  126.                     is = conn.getInputStream();   
  127.                     byte[] buffer = new byte[1024];   
  128.                     int len = -1;   
  129.                     while ((len = is.read(buffer)) != -1) {   
  130.                         accessFile.write(buffer, 0, len);   
  131.                     }   
  132.                 } catch (IOException e) {   
  133.                     e.printStackTrace();   
  134.                 } finally {   
  135.                     try {   
  136.                         if(is != null) {   
  137.                             is.close();   
  138.                         }     
  139.                     } catch (Exception ex) {   
  140.                         ex.printStackTrace();   
  141.                     }   
  142.                 }   
  143.             }   
  144.         }   
  145.     }   
复制代码
第二种就不是同源多线程

显而易见的 这两种肯定都比单线程快

但是如果我写这段代码的话 我懒得用多线程

zhouyiran2
Sorta 发表于 2014-1-5 16:49
第一种多线程同源多线程第二种就不是同源多线程

显而易见的 这两种肯定都比单线程快

1.我问你,is = conn.getInputStream();  需要时间么?如果一个文件只有几百B或几K,你说哪个效率高?建立Http连接、创建线程都是要系统资源、时间的!

Sorta
zhouyiran2 发表于 2014-1-5 16:58
1.我问你,is = conn.getInputStream();  需要时间么?如果一个文件只有几百B或几K,你说哪个效率高?建立 ...

请自行尝试

zhouyiran2
Sorta 发表于 2014-1-5 17:00
请自行尝试

你自行尝试过吗?

huanghongxun
表示只是为了省事不想写多线程。

zhh0000zhh
刚看到。。。
感谢你的好意。。
不过我刚才试了一下,多线程下载对于大资源文件加速极为明显,最大可达到理论速度(线程数*单线程速度)
至于小文件从没想过使用多线程。。。

zhouyiran2
zhh0000zhh 发表于 2014-4-26 10:45
刚看到。。。
感谢你的好意。。
不过我刚才试了一下,多线程下载对于大资源文件加速极为明显,最大可达到理 ...

谢谢,那个已经经过了实验,证明速度慢和我的下载器的缺陷有一定关系,准备对WebClient进行改造来避免问题

zhh0000zhh
zhouyiran2 发表于 2014-4-26 11:01
谢谢,那个已经经过了实验,证明速度慢和我的下载器的缺陷有一定关系,准备对WebClient进行改造来避免问 ...

WebClient没有任何前途。。。
用httpresponse自己写吧

zhouyiran2
zhh0000zhh 发表于 2014-4-26 14:25
WebClient没有任何前途。。。
用httpresponse自己写吧

我说的是WebClient的网络部分写得还是可以的,至少效果比我自己写出来的好,准备好好看看到底差别问题出在哪里

zhh0000zhh
zhouyiran2 发表于 2014-4-26 15:23
我说的是WebClient的网络部分写得还是可以的,至少效果比我自己写出来的好,准备好好看看到底差别问题出在 ...

好吧。。。。。