基于HTTP协议的多线程下载he断点续传的实现
Android下使用Http协议实现多线程断点续传下载

0.使用多线程下载会提升文件下载的速度,那么多线程下载文件的过程是:(1)首先获得下载文件的长度,然后设置本地文件的长度HttpURLConnection.getContentLength();RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");file.setLength(filesize);//设置本地文件的长度(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。
例如10M大小,使用3个线程来下载,线程下载的数据长度 (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M下载开始位置:线程id*每条线程下载的数据长度 = ?下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?(3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止代码如下:HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");(4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");threadfile.seek(2097152);//从文件的什么位置开始写入数据1.多线程下载的核心代码示例Java代码1.public class MulThreadDownload2.{3. /**4. * 多线程下载5. * @param args6. */7. public static void main(String[] args)8. {9. String path = "/QQWubiSetup.exe";10. try11. {12. new MulThreadDownload().download(path, 3);13. }14. catch (Exception e)15. {16. e.printStackTrace();17. }18. }19. /**20. * 从路径中获取文件名称21. * @param path 下载路径22. * @return23. */24. public static String getFilename(String path)25. {26. return path.substring(stIndexOf('/')+1);27. }28. /**29. * 下载文件30. * @param path 下载路径31. * @param threadsize 线程数32. */33. public void download(String path, int threadsize) throwsException34. {35. URL url = new URL(path);36. HttpURLConnection conn = (HttpURLConnection)url.openConnection();37. conn.setRequestMethod("GET");38. conn.setConnectTimeout(5 * 1000);39. //获取要下载的文件的长度40. int filelength = conn.getContentLength();41. //从路径中获取文件名称42. String filename = getFilename(path);43. File saveFile = new File(filename);44. RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");45. //设置本地文件的长度和下载文件相同46. accessFile.setLength(filelength);47. accessFile.close();48. //计算每条线程下载的数据长度49. int block = filelength%threadsize==0? filelength/threadsize : filelength/threadsize+1;50. for(int threadid=0 ; threadid < threadsize ; threadid++){51. new DownloadThread(url, saveFile, block, threadid).start();52. }53. }54.55. private final class DownloadThread extends Thread56. {57. private URL url;58. private File saveFile;59. private int block;//每条线程下载的数据长度60. private int threadid;//线程id61. public DownloadThread(URL url, File saveFile, int block, int threadid)62. {63. this.url = url;64. this.saveFile = saveFile;65. this.block = block;66. this.threadid = threadid;67. }68.@Override69. public void run()70. {71. //计算开始位置公式:线程id*每条线程下载的数据长度= ?72. //计算结束位置公式:(线程id +1)*每条线程下载的数据长度-1 =?73. int startposition = threadid * block;74. int endposition = (threadid + 1 ) * block - 1;75. try76. {77. RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");78. //设置从什么位置开始写入数据79. accessFile.seek(startposition);80. HttpURLConnection conn = (HttpURLConnection)url.openConnection();81. conn.setRequestMethod("GET");82. conn.setConnectTimeout(5 * 1000);83. conn.setRequestProperty("Range", "bytes="+ startposition+ "-"+ endposition);84. InputStream inStream = conn.getInputStream();85. byte[] buffer = new byte[1024];86. int len = 0;87. while( (len=inStream.read(buffer)) != -1 )88. {89. accessFile.write(buffer, 0, len);90. }91. inStream.close();92. accessFile.close();93. System.out.println("线程id:"+ threadid+ "下载完成");94. }95. catch (Exception e)96. {97. e.printStackTrace();98. }99. }100. }101.}2.多线程断点下载功能,这里把断点数据保存到数据库中:注意代码注释的理解(0)主Activity,关键点使用Handler更新进度条与开启线程下载避免ANR若不使用Handler却要立即更新进度条数据,可使用://resultView.invalidate(); UI线程中立即更新进度条方法//resultView.postInvalidate(); 非UI线程中立即更新进度条方法Java代码1./**2. * 注意这里的设计思路,UI线程与参数保存问题,避免ANR问题,UI控件的显示3. * @author kay4. *5. */6.public class DownloadActivity extends Activity7.{8. private EditText downloadpathText;9. private TextView resultView;10. private ProgressBar progressBar;11.12.@Override13. public void onCreate(Bundle savedInstanceState)14. {15. super.onCreate(savedInstanceState);16. setContentView(yout.main);17.18. downloadpathText = (EditText) this.findViewById(R.id.downloadpath);19. progressBar = (ProgressBar) this.findViewById(R.id.downloadbar);20. resultView = (TextView) this.findViewById(R.id.result);21. Button button = (Button) this.findViewById(R.id.button);22. button.setOnClickListener(new View.OnClickListener()23. {24.@Override25. public void onClick(View v)26. {27. //取得下载路径28. String path = downloadpathText.getText().toString();29. //判断SDCard是否存在30. if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))31. {32. //下载操作33. download(path, Environment.getExternalStorageDirectory());34. }35. else36. {37. Toast.makeText(DownloadActivity.this, R.string.sdcarderror, 1).show();38. }39. }40. });41. }42. //主线程(UI线程)43. //业务逻辑正确,但是该程序运行的时候有问题(可能出现ANR问题)44. //对于显示控件的界面更新只是由UI线程负责,如果是在非UI线程更新控件的属性值,更新后的显示界面不会反映到屏幕上45. /**46. * 参数类型:因为启动一个线程还需要使用到上面方法的参数,而主方法启动后很快就会销毁,47. * 那么使用Final可以解决参数丢失的问题48. * path 注意是Final类型49. * savedir 注意是Final类型50. */51. private void download(final String path, final File savedir)52. {53. //这里开启一个线程避免ANR错误54. new Thread(new Runnable()55. {56.@Override57. public void run()58. {59. FileDownloader loader = new FileDownloader(DownloadActivity.this, path, savedir, 3);60. //设置进度条的最大刻度为文件的长度61. progressBar.setMax(loader.getFileSize());62. try63. {64. loader.download(new DownloadProgressListener()65. {66. /**67. * 注意这里的设计,显示进度条数据需要使用Handler来处理68. * 因为非UI线程更新后的数据不能被刷新69. */70.@Override71. public void onDownloadSize(int size)72. {73. //实时获知文件已经下载的数据长度74. Message msg = new Message();75. //设置消息标签76. msg.what = 1;77. msg.getData().putInt("size", size);78. //使用Handler对象发送消息79. handler.sendMessage(msg);80. }81. });82. }83. catch (Exception e)84. {85. //发送一个空消息到消息队列86. handler.obtainMessage(-1).sendToTarget();87. /**88. * 或者使用下面的方法发送一个空消息89. * Message msg = new Message();90. * msg.what = 1;91. * handler.sendMessage(msg);92. */93. }94. }95. }).start();96. }97.98.99. /**Handler原理:当Handler被创建时会关联到创建它的当前线程的消息队列,该类用于往消息队列发送消息100. *101. * 消息队列中的消息由当前线程内部进行处理102. */103. private Handler handler = new Handler()104. {105. //重写Handler里面的handleMessage方法处理消息106.@Override107. public void handleMessage(Message msg)108. {109. switch (msg.what)110. {111. case 1:112. //进度条显示113. progressBar.setProgress(msg.getData().getInt("size"));114. float num = (float)progressBar.getProg ress()/(float)progressBar.getMax();115. int result = (int)(num*100);116. //resultView.invalidate(); UI线程中立即更新进度条方法117. //resultView.postInvalidate(); 非UI线程中立即更新进度条方法118. resultView.setText(result+ "%"); 119. //判断是否下载成功120. if(progressBar.getProgress()==progress Bar.getMax())121. {122. Toast.makeText(DownloadActivity.th is, R.string.success, 1).show();123. }124. break;125. case -1:126. Toast.makeText(DownloadActivity.this, R.string.error, 1).show();127. break;128. }129. }130. };131.132.}(1)下载类:注意计算每条线程的下载长度与下载起始位置的方法Java代码1.public class DownloadThread extends Thread2.{3. private static final String TAG = "DownloadThread";4. private File saveFile;5. private URL downUrl;6. private int block;7. //下载开始位置8. private int threadId = -1;9. //下载文件长度10. private int downLength;11. private boolean finish = false;12. private FileDownloader downloader;13. public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId)14. {15. this.downUrl = downUrl;16. this.saveFile = saveFile;17. this.block = block;18. this.downloader = downloader;19. this.threadId = threadId;20. this.downLength = downLength;21. }22.23.@Override24. public void run()25. {26. //未下载完成27. if(downLength < block)28. {29. try30. {31. HttpURLConnection http = (HttpURLConnection)downUrl.openConnection();32. http.setConnectTimeout(5 * 1000);33. http.setRequestMethod("GET");34. http.setRequestProperty("Accept", "image/gif,image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave -flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, applicati on/vnd.ms-excel, application/vnd.ms-powerpoint, application/ms word, */*");35. http.setRequestProperty("Accept-Language", "zh-CN");36. http.setRequestProperty("Referer", downUrl.toString());37. http.setRequestProperty("Charset", "UTF-8");38. //下载开始位置:线程id*每条线程下载的数据长度 = ?39. int startPos = block * (threadId - 1) + downLength;40. //下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?41. int endPos = block * threadId -1;42. //设置获取实体数据的范围43. http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);44. http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");45. http.setRequestProperty("Connection", "Keep-Alive");46.47. InputStream inStream = http.getInputStream();48. byte[] buffer = new byte[1024];49. int offset = 0;50. print("Thread " + this.threadId + " start download from position "+ startPos);51. RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");52. threadfile.seek(startPos);53. while ((offset = inStream.read(buffer, 0, 1024)) != -1)54. {55. threadfile.write(buffer, 0, offset);56. downLength += offset;57. downloader.update(this.threadId, downLength);58. downloader.append(offset);59. }60. threadfile.close();61. inStream.close();62. print("Thread " + this.threadId + " downloadfinish");63. //标记是否完成64. this.finish = true;65. }66. catch (Exception e)67. {68. this.downLength = -1;69. print("Thread "+ this.threadId+ ":"+ e);70. }71. }72. }73.74. private static void print(String msg)75. {76. Log.i(TAG, msg);77. }78.79. /**80. * 下载是否完成81. * @return82. */83. public boolean isFinish()84. {85. return finish;86. }87.88. /**89. * 已经下载的内容大小90. * @return 如果返回值为-1,代表下载失败91. */92. public long getDownLength()93. {94. return downLength;95. }96.}文件下载器,使用Java代码1./**2. * 文件下载器,使用这个类的方法如下示例:3. * FileDownloader loader = new FileDownloader(context, "http:///ejb3/ActivePort.exe",4. * new File("D:\\androidsoft\\test"), 2);5. * loader.getFileSize();//得到文件总大小6. * try {7. * loader.download(new DownloadProgressListener(){8. * public void onDownloadSize(int size) {9. * print("已经下载:"+ size);10. * }11. * });12. * }13. * catch (Exception e)14. * {15. * e.printStackTrace();16. * }17. */18.public class FileDownloader19.{20. private static final String TAG = "FileDownloader";21. private Context context;22. private FileService fileService;23. //已下载文件长度24. private int downloadSize = 0;25. //原始文件长度26. private int fileSize = 0;27. ///线程数28. private DownloadThread[] threads;29. //本地保存文件30. private File saveFile;31. //缓存各线程下载的长度32. private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();33. //每条线程下载的长度34. private int block;35. //下载路径36. private String downloadUrl;37. //获取线程数38. public int getThreadSize()39. {40. return threads.length;41. }42. /**43. * 获取文件大小44. * @return45. */46. public int getFileSize()47. {48. return fileSize;49. }50. /**51. * 累计已下载大小52. * @param size53. */54. protected synchronized void append(int size)55. {56. downloadSize += size;57. }58. /**59. * 更新指定线程最后下载的位置60. * @param threadId 线程id61. * @param pos 最后下载的位置62. */63. protected synchronized void update(int threadId, int pos)64. {65. this.data.put(threadId, pos);66. this.fileService.update(this.downloadUrl, this.data);67. }68. /**69. * 文件下载构造器70. * @param downloadUrl 下载路径71. * @param fileSaveDir 文件保存目录72. * @param threadNum 下载线程数73. */74. public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum)75. {76. try77. {78. this.context = context;79. this.downloadUrl = downloadUrl;80. fileService = new FileService(this.context);81. URL url = new URL(this.downloadUrl);82. if(!fileSaveDir.exists()) fileSaveDir.mkdirs();83. this.threads = new DownloadThread[threadNum];84. HttpURLConnection conn = (HttpURLConnection) url.openConnection();85. conn.setConnectTimeout(5*1000);86. conn.setRequestMethod("GET");87. conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-fla sh, application/xaml+xml, application/vnd.ms-xpsdocument, appl ication/x-ms-xbap, application/x-ms-application, application/v nd.ms-excel, application/vnd.ms-powerpoint, application/msword , */*");88. conn.setRequestProperty("Accept-Language", "zh-CN");89. conn.setRequestProperty("Referer", downloadUrl);90. conn.setRequestProperty("Charset", "UTF-8");91. conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR1.1.4322; .NET CLR2.0.50727; .NET CLR3.0.04506.30; .NET CLR3.0.4506.2152; .NET CLR 3.5.30729)");92. conn.setRequestProperty("Connection", "Keep-Alive");93. conn.connect();94. printResponseHeader(conn);95. if (conn.getResponseCode()==200)96. {97. //根据响应获取文件大小98. this.fileSize = conn.getContentLength();99. if (this.fileSize <= 0) throw new RuntimeException("Unkown file size ");100. //获取文件名称101. String filename = getFileName(conn); 102. //构建保存文件103. this.saveFile = new File(fileSaveDir, file name);104. //获取下载记录105. Map<Integer, Integer> logdata = fileService.getData(downloadUrl);106. //如果存在下载记录107. if(logdata.size()>0)108. {109. //把各条线程已经下载的数据长度放入data 中110. for(Map.Entry<Integer, Integer> entry : logdata.entrySet())111. data.put(entry.getKey(), entry.get Value());112. }113. //下面计算所有线程已经下载的数据长度114. if(this.data.size()==this.threads.length)115. {116. for (int i = 0; i < this.threads.lengt h; i++)117. {118. this.downloadSize += this.data.get (i+1);119. }120. print("已经下载的长度"+ this.downloadSize);121. }122. //计算每条线程下载的数据长度123. this.block = (this.fileSize % this.threads .length)==0? this.fileSize / this.threads.length : this.fileSi ze / this.threads.length + 1;124. }125. else126. {127. throw new RuntimeException("server no resp onse ");128. }129. }130. catch (Exception e)131. {132. print(e.toString());133. throw new RuntimeException("don't connection t his url");134. }135. }136. /**137. * 获取文件名138. */139. private String getFileName(HttpURLConnection conn) 140. {141. String filename = this.downloadUrl.substring(this.stIndexOf('/') + 1);142. if(filename==null || "".equals(filename.trim())){/ /如果获取不到文件名称143. for (int i = 0;; i++) {144. String mine = conn.getHeaderField(i); 145. if (mine == null) break;146. if("content-disposition".equals(conn.getHe aderFieldKey(i).toLowerCase())){147. Matcher m = pile(".*filenam e=(.*)").matcher(mine.toLowerCase());148. if(m.find()) return m.group(1); 149. }150. }151. filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名152. }153. return filename;154. }155.156. /**157. * 开始下载文件158. * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null159. * @return 已下载文件大小160. * @throws Exception161. */162. public int download(DownloadProgressListener listener) throws Exception{163. try164. {165. //创建本地文件166. RandomAccessFile randOut = new RandomAccessFil e(this.saveFile, "rw");167. if(this.fileSize>0) randOut.setLength(this.fil eSize);168. randOut.close();169. URL url = new URL(this.downloadUrl);170. if(this.data.size() != this.threads.length) 171. {172. this.data.clear();173. for (int i = 0; i < this.threads.length; i ++)174. {175. //初始化每条线程已经下载的数据长度为176. this.data.put(i+1, 0);177. }178. }179. //开启线程进行下载180. for (int i = 0; i < this.threads.length; i++)181. {182. int downLength = this.data.get(i+1); 183. //判断线程是否已经完成下载,否则继续下载184. if(downLength < this.block && this.downloa dSize<this.fileSize)185. {186. this.threads[i] = new DownloadThread(t his, url, this.saveFile, this.block, this.data.get(i+1), i+1);187. this.threads[i].setPriority(7);//可删除这条188. this.threads[i].start();189. }190. else191. {192. this.threads[i] = null;193. }194. }195. this.fileService.save(this.downloadUrl, this.d ata);196. //下载未完成197. boolean notFinish = true;198. // 循环判断所有线程是否完成下载199. while (notFinish)200. {201. Thread.sleep(900);202. //假定全部线程下载完成203. notFinish = false;204. for (int i = 0; i < this.threads.length; i ++)205. {206. //如果发现线程未完成下载207. if (this.threads[i] != null && !this.t hreads[i].isFinish())208. {209. //设置标志为下载没有完成210. notFinish = true;211. //如果下载失败,再重新下载212. if(this.threads[i].getDownLength() == -1)213. {214. this.threads[i] = new Download Thread(this, url, this.saveFile, this.block, this.data.get(i+1 ), i+1);215. this.threads[i].setPriority(7) ;216. this.threads[i].start(); 217. }218. }219. }220. //通知目前已经下载完成的数据长度221. if(listener!=null) listener.onDownloadSize (this.downloadSize);222. }223. //删除数据库中下载信息224. fileService.delete(this.downloadUrl);225. }226. catch (Exception e)227. {228. print(e.toString());229. throw new Exception("file download fail"); 230. }231. return this.downloadSize;232. }233.234. /**235. * 获取Http响应头字段236. * @param http237. * @return238. */239. public static Map<String, String> getHttpResponseHeade r(HttpURLConnection http) {240. Map<String, String> header = new LinkedHashMap<Str ing, String>();241. for (int i = 0;; i++) {242. String mine = http.getHeaderField(i); 243. if (mine == null) break;244. header.put(http.getHeaderFieldKey(i), mine);245. }246. return header;247. }248. /**249. * 打印Http头字段250. * @param http251. */252. public static void printResponseHeader(HttpURLConnecti on http)253. {254. Map<String, String> header = getHttpResponseHeader (http);255. for(Map.Entry<String, String> entry : header.entry Set())256. {257. String key = entry.getKey()!=null ? entry.getK ey()+ ":" : "";258. print(key+ entry.getValue());259. }260. }261. private static void print(String msg)262. {263. Log.i(TAG, msg);264. }265.}Java代码1.public interface DownloadProgressListener2.{3. public void onDownloadSize(int size);4.}(2)文件操作,断点数据库存储Java代码1.public class DBOpenHelper extends SQLiteOpenHelper2.{3. private static final String DBNAME = "itcast.db";4. private static final int VERSION = 1;5.6. public DBOpenHelper(Context context)7. {8. super(context, DBNAME, null, VERSION);9. }10.11.@Override12. public void onCreate(SQLiteDatabase db)13. {14. db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)");15. }16.@Override17. public void onUpgrade(SQLiteDatabase db, int oldVersion,int newVersion)18. {19. db.execSQL("DROP TABLE IF EXISTS filedownlog");20. onCreate(db);21. }22.}Java代码1./**2. * 文件下载业务bean3. */4.public class FileService5.{6. private DBOpenHelper openHelper;7. public FileService(Context context)8. {9. openHelper = new DBOpenHelper(context);10. }11. /**12. * 获取每条线程已经下载的文件长度13. * @param path14. * @return15. */16. public Map<Integer, Integer> getData(String path)17. {18. SQLiteDatabase db = openHelper.getReadableDatabase();19. Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path});20. Map<Integer, Integer> data = new HashMap<Integer, Integer>();21. while(cursor.moveToNext())22. {23. data.put(cursor.getInt(0), cursor.getInt(1));24. }25. cursor.close();26. db.close();27. return data;28. }29. /**30. * 保存每条线程已经下载的文件长度31. * @param path32. * @param map33. */34. public void save(String path, Map<Integer, Integer> map)35. {//int threadid, int position36. SQLiteDatabase db = openHelper.getWritableDatabase();37. db.beginTransaction();38. try39. {40. for(Map.Entry<Integer, Integer> entry : map.entrySet())41. {42. db.execSQL("insert into filedownlog(downpath,threadid, downlength) values(?,?,?)",43. new Object[]{path, entry.getKey(), entry.getValue()});44. }45. db.setTransactionSuccessful();46. }47. finally48. {49. db.endTransaction();50. }51. db.close();52. }53. /**54. * 实时更新每条线程已经下载的文件长度55. * @param path56. * @param map57. */58. public void update(String path, Map<Integer, Integer> map)59. {60. SQLiteDatabase db = openHelper.getWritableDatabase();61. db.beginTransaction();62. try{63. for(Map.Entry<Integer, Integer> entry : map.entrySet()){64. db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?",65. new Object[]{entry.getValue(), path,entry.getKey()});66. }67. db.setTransactionSuccessful();68. }finally{69. db.endTransaction();70. }71. db.close();72. }73. /**74. * 当文件下载完成后,删除对应的下载记录75. * @param path76. */77. public void delete(String path)78. {79. SQLiteDatabase db = openHelper.getWritableDatabase();80. db.execSQL("delete from filedownlog where downpath=?", new Object[]{path});81. db.close();82. }83.}。
基于多线程的断点续传实现

基于多线程的断点续传实现
周翔;阮世颖
【期刊名称】《电脑知识与技术》
【年(卷),期】2007(000)022
【摘要】网络的不稳定常常造成数据传送的不稳定,为了获得可靠、高效的数据传输,断点续传技术应运而生.本文介绍了所开发的一种基于多线程的文件传输系统,具有断点续传的功能,详细给出了这个系统的设计方案和所用到的关键技术.
【总页数】3页(P1000-1002)
【作者】周翔;阮世颖
【作者单位】南昌大学软件学院,江西南昌,330047;南昌大学医学院,江西南
昌,330006
【正文语种】中文
【中图分类】TP311
【相关文献】
1.用VC实现基于TCP/IP的点对点多线程断点续传 [J], 刘洪旭;徐国天
2.一种基于SIP和MSRP协议实现文件断点续传的方法 [J], 罗有平;周炳然
3.基于多线程的断点续传实现 [J], 周翔;阮世颖
4.基于HTML5大文件断点续传的实现方案 [J], 王莉敏;梁正和;段全锋
5.基于Android平台多线程断点续传技术研究 [J], 石建华;聂文芳;文晓棠
因版权原因,仅展示原文概要,查看原文内容请购买。
[VC++]点对点(P2P)多线程断点续传的实现
![[VC++]点对点(P2P)多线程断点续传的实现](https://img.taocdn.com/s3/m/1f65af49b307e87101f69655.png)
fname=zmfile[n].name;
CString tmep;
//初使化文件名
tmep.Format("\\temp\\%s",fname);
//给主函数发消息
CString aaa;
aaa="正在读取 "+fname+" 信息,马上开始下载。。。\n";
AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
aaa.ReleaseBuffer();
//如果文件长度小于0就返回
我写了一个以此为基础的实用程序(网络传圣,包含源代码),可用了基于TCP/IP的电脑上,供大家学习。
实现方法(VC++,基于TCP/IP协议)如下:
仍釆用服务器与客户模式,需分别对其设计与编程。
服务器端较简单,主要就是加入待传文件,监听客户,和传送文件。而那些断点续传的功能,以及文件的管理都放在客户端上。
//发文件消息给主函数
aaa.Format("%s 文件被请求!%s\n",zmfile[fiinfo->fileno].name,nameph[fiinfo->fileno]);
AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);
aa.ReleaseBuffer();
DWORD dwthread;
//建立用户线程
::CreateThread(NULL,0,clientthread,(LPVOID)s1,0,&dwthread);
C#实现支持断点续传多线程下载的 Http Web 客户端工具类

Throw,
CancelAll,
Ignore,
Retry
}
/// <summary>
/// 包含 Exception 事件数据的类
/// </summary>
public class ExceptionEventArgs : System.EventArgs
{
private System.Exception _Exception;
private ExceptionActions _ExceptionAction;
private DownLoadState _DownloadState;
public DownLoadState DownloadState
namespace Microshaoft.Utils
{
using System;
using System.IO;
using ;
using System.Text;
using System.Security;
using System.Threading;
{
get
{
return _ExceptionAction;
}
set
{
_ExceptionAction = value;
}
}
internal ExceptionEventArgs(System.Exception e, DownLoadState DownloadState)
this._Position = Position;
this._Data = Data;
this._Length = Length;
web端断点续传研究

假如一个文件有1000个字节,那么其范围就是0-999,则:
Range: bytes=500表示读取该文件的500-999字节,共500字节。 Range: bytes=500-599 表示读取该文件的500-599字节,共100字节。 Range还有其它几种写法,但上面这两种是最常用的,对于断点续传也足矣了。如果HTTP 请求中包含Range字段,那么服务器会返回206(Partial Content),同时HTTP头中也会有一个相 应的Content-Range字段,类似下面的格式: Content-Range: bytes 500-999/1000 Content-Range字段说明服务器返回了文件的某个范围及文件的总长度。这时ContentLength字段就不是整个文件的大小了,而是对应文件这个范围的字节数,这一点一定要注意。
2.应用分析
• 2.1.web端上传下载 • 2.2.客户端上传下载
2.1.web端上传下载
• • • • • • • • • • • • • • • • • • • • • • • • 下载实例 $fname = './MMLDZG.mp3'; $fp = fopen($fname,'rb'); $fsize = filesize($fname); if (isset($_SERVER['HTTP_RANGE']) && ($_SERVER['HTTP_RANGE'] != "") && preg_match("/^bytes=([0-9]+)-$/i", $_SERVER['HTTP_RANGE'], $match) && ($match[1] < $fsize)) { $start = $match[1]; } else { $start = 0; } header("Cache-control: public"); header("Pragma: public"); if ($star > 0) { fseek($fp, $start); ‘ Header("HTTP/1.1 206 Partial Content"); Header("Content-Length: " . ($fsize - $start)); Header("Content-Ranges: bytes" . $start . "-" . ($fsize - 1) . "/" . $fsize); } else { header("Content-Length: $fsize"); Header("Accept-Ranges: bytes"); } header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment;filename=mmdld.mp3"); fpassthru($fp); fclose($fp);
断点续传方案

断点续传方案简介断点续传是指在网络传输过程中,当连接中断或者文件传输中止时,能够从中断处重新开始传输,而不是从头开始。
这样可以提高文件传输的可靠性和传输效率。
在实际应用中,断点续传方案常常用于大文件的上传或下载过程中,以确保用户在网络不稳定的情况下能够顺利完成文件传输,而无需重新开始。
本文将介绍几种常见的断点续传方案,并分析各种方案的优缺点,帮助读者选择适合自己应用场景的方案。
方案一:基于HTTP的断点续传HTTP协议是应用层协议中最常用的协议之一,支持断点续传的HTTP服务器通常会在响应头中添加Range字段,用于指定服务器传输的起始位置。
客户端在进行文件下载时,通过设置请求头中的Range字段来请求指定范围的数据。
服务器接收到请求后,根据Range字段返回相应的数据片段。
如果客户端在下载过程中中断,可以通过设置Range字段重新发送请求,从中断处继续下载。
HTTP的断点续传方案具有以下优点:-:基于HTTP的断点续传方案使用标准的HTTP协议,不需要额外的协议和框架支持,方便快捷。
-:基于HTTP的断点续传方案通常兼容多种操作系统和终端设备,使用广泛。
-:通过设置不同的Range字段,可以实现下载指定范围的数据,具有较高的灵活性。
-:HTTP协议本身就具有较高的可靠性,断点续传方案在一定程度上增强了文件传输的可靠性。
然而,基于HTTP的断点续传方案也存在一些局限性:-:由于每次续传都需要从中断处开始,可能会导致重复传输已经传输过的数据,降低传输效率。
-:对于非常大的文件,服务器需要保存大量的中断点信息,占用较多的磁盘空间和内存资源。
-:如果服务器不支持断点续传,那么即使客户端实现了断点续传方案,也无法成功续传。
方案二:基于FTP的断点续传FTP(File Transfer Protocol)是一种文件传输协议,也常用于文件上传和下载。
FTP支持断点续传的机制,能够在网络中断或传输中止后从中断处继续传输。
java下载功能

java下载功能Java是一种跨平台的编程语言,能够在各种操作系统上运行,因此非常适合用于开发具有下载功能的应用程序。
Java提供了丰富的API和库,使开发者能够轻松地实现下载功能。
下面是一些常用的Java下载功能的实现方式:1. 使用URLConnection类来进行下载:URLConnection类是Java提供的用于网络连接的类,可以通过该类来实现下载。
使用URLConnection的步骤包括创建URL对象、打开连接、获取输入流、创建输出流、将数据从输入流写入输出流等。
2. 使用HttpClient库来进行下载:HttpClient是一个功能强大的开源HTTP客户端库,可以用于发送HTTP请求并处理HTTP响应。
通过使用HttpClient库,可以实现更为复杂的下载功能,例如断点续传、多线程下载等。
3. 使用多线程来进行下载:在下载大文件时,为了加快下载速度,可以使用多线程来并行下载。
通过将文件分成多个部分,每个部分由一个线程负责下载,可以同时下载多个部分,提高下载速度。
4. 使用下载管理器来进行下载:下载管理器是一种用于管理下载任务的工具,可以对下载任务进行管理和控制,例如暂停、取消、进度监控等。
Java提供了一些第三方库,如Downpour 和Download4j,可以用于实现下载管理器。
5. 使用流式处理来进行下载:在Java中,可以使用流式处理来处理大文件的下载。
通过使用BufferedInputStream和BufferedOutputStream,可以将下载的文件分块读取并写入本地文件,避免一次性读取整个文件导致内存溢出。
总之,Java提供了多种方式来实现下载功能,开发者可以根据需求选择合适的方法来实现。
通过合理利用Java的API和库,能够实现高效、安全的下载功能,并提供给用户优质的下载体验。
用Java实现HTTP断点续传——多线程下载文件

用Java实现HTTP断点续传——多线程下载文件钟华本文介绍了一种利用Java 来实现断点续传的方法。
断点续传的原理Http断点续传的原理其实很简单,就是在请求上和一般的下载有所不同而已。
例如浏览器请求服务器上的一个文件时,所发出的请求如下(假设服务器域名为,文件名为down.zip):GET /down.zip HTTP/1.1Accept: image/gif, image/x-xbitmap, image/jpeg, image/jpg,application/vnd.ms-excel, application/msword,application/vnd.ms-powerpoint, */*Accept-Language: zh-cnAccept-Encoding: gzip, deflautUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) Connection: Keep-Alive服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:200Content-Length=106786028Accept-Ranges=bytesDate=Mon, 30 Apr 2001 12:56:11 GMTETag=W/"02ca57e173c11:95b"Content-Type=application/octet-streamServer=Microsoft-IIS/5.0Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT所谓断点续传,就是从文件已经下载的地方开始继续下载。
所以在客户端浏览器传给Web服务器的时候要多加一条信息——从哪里开始。
下面是用自己编的一个“浏览器”来传递请求信息给Web服务器,要求从2000070字节开始。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
毕业设计(论文)题目基于HTTP协议的多线程下载和断点续传的实现学生姓名学号专业计算机科学与技术班级指导教师评阅教师完成日期年月日学位论文原创性声明本人郑重声明:所呈交的论文是本人在导师的指导下独立进行研究所取得的研究成果。
除了文中特别加以标注引用的内容外,本论文不包含任何其他个人或集体已经发表或撰写的成果作品。
本人完全意识到本声明的法律后果由本人承担。
作者签名:年月日学位论文版权使用授权书本学位论文作者完全了解学校有关保障、使用学位论文的规定,同意学校保留并向有关学位论文管理部门或机构送交论文的复印件和电子版,允许论文被查阅和借阅。
本人授权省级优秀学士学位论文评选机构将本学位论文的全部或部分内容编入有关数据库进行检索,可以采用影印、缩印或扫描等复制手段保存和汇编本学位论文。
本学位论文属于1、保密□,在_________年解密后适用本授权书。
2、不保密□。
(请在以上相应方框内打“√”)作者签名:年月日导师签名:年月日目录摘要 (1)前言 (2)1 HTTP协议 (3)1.1HTTP协议的发展 (3)1.2HTTP协议的特点 (4)1.3HTTP会话及报文格式 (6)2 Windows套接字 (8)2.1 什么是套接字 (8)2.2 套接字规范 (9)2.3 Windows套接字的发展 (11)2.4 套接字的使用和WinSock API (12)3 多线程及断点续传技术 (15)3.1 多线程的优点 (18)3.2 多线程之间的互斥和同步 (16)3.3 什么是断点续传技术 (17)4 下载工具的设计与实现 (18)4.1 基本结构与数据流程图 (18)4.2 程序基本功能设计与实现 (19)4.3 代码分析 (24)4.4主要功能实现算法 (28)5 总结 (35)致谢 (36)参考文献 (37)附录 (38)基于HTTP协议的多线程下载和断点续传的实现学生:叶升路指导教师:覃颖(三峡大学电气信息学院)摘要:本文介绍了网络下载软件中的最新技术——多线程下载和断点续传技术,同时也介绍了HTTP协议的发展、特点以及WinSock编程技术。
最后在这些技术的基础上成功设计并实现了基于HTTP协议的具有多线程下载和断点续传功能的下载软件。
本软件的实现代码未使用任何WinInet API函数如InternetOpen , InternetConnect等,而是直接使用WinSock编程,逐步解析HTTP协议来完成会话和文件下载等功能。
经测试,下载速度有所提高。
关键词:下载;多线程;断点续传;HTTP;WinSock;Abstract:This paper introduces the latest downloading technology called multi-threaded downloading and resume in network downloading software. But also descripes the development of HTTP protocol, characteristics and WinSock programming. Finally, based on these technologies successfully designed and implemented a downloading tool based on the HTTP protocol with multi-threaded and resume features. The realization of the software code does not use any WinInet API functions such as InternetOpen, InternetConnect, etc., but directly use WinSock to programming, and complete the functions of conversation and file downloads and others by parse HTTP protocol steply. After tested, the speed of downloading has increased.Keywords:Downloading;Multi-thread;Resume;HTTP;WinSock;前言最近几年,随着计算机网络的飞速发展,因特网(Internet)已经逐渐成为人们生活、工作、学习必不可缺的一部分。
因特网上存储了大量丰富的信息资源,我们可以使用下载工具,把需要的信息资源下载到本地。
但是由于受到各种因素的限制,例如服务器性能、网络带宽、下载的信息量以及下载工具等等,下载速度受到不同程度的影响。
因此人们不断地提高服务器性能,扩展网络带宽,开发效率更高的下载工具以达到最大化提高下载速度的目的。
在限制下载速度的众多因素中,研究新的网络下载技术开发出更高效的下载工具无疑是其中最节约,环保以及方便的方式。
网络下载技术,也可以称为网络文件共享技术,它一直是网络发展的重要推动力之一。
早期人们共享资源的普遍方法是将资源文件上传至服务器上,然后其他用户可以通过HTTP或FTP等协议将其下载到本地电脑。
这种模式称为客户机/服务器模式即C/S模式,它对服务器的依赖性很大,当下载用户很少时,比如说一个,他将独享服务器的带宽,很显然其下载速度会非常快。
然而当下载的人数较多而服务器带宽有限时,比如服务器带宽为3MB/S,而下载人数为100人,则众多下载用户不得不共享一个带宽(3MB/S)最终结果是下载速度均分(30KB/S),普遍不高。
P2P技术的出现使得人们终于摆脱了服务器的枷锁。
它的主要特点是资源分散、负载均衡、和非中心化,它将共享的文件存储在各个客户机节点上,用户之间可以直接共享和传输文件而不需要通过服务器。
客户机不再只利用服务器带宽进行下载,它同时也可以利用其他客户机节点的带宽,这样大大提高了下载速度。
纵观网络下载技术发展的历史,可以将其划分为四个阶段:单线程下载阶段、多线程下载及断点续传阶段、P2P阶段、P2SP阶段。
一、单线下载时代:应对有限时间流量的办法早在上个世纪90年代,当时互联网并不普及,很多人使用Modem拨号,通过Telnet 软件连接到拨接式BBS上获取资讯并与别人交流(收发邮件等),由于服务器的电话线路数量有限,因此都会限制连接时间,一般新注册用户只有10分钟左右。
这点时间用来看帖回帖显然不够的,因此有人就开发了软件,进入BBS后,能够将整个BBS上所有内容都下载回来,然后可以断线慢慢看慢慢回,最后再次拨入BBS上传回复。
二、断点续传与多线程下载时代:大幅度提高速度进入Windows与WWW(World Wide Web,互联网)时代之后,IE,Netscape等浏览器都可以通过点击左键下载,那个时候网络速度最快不过5KB/s,下载一首5MB的MP3歌曲要15分钟以上!中途万一断线就前功尽弃,于是有人开发了支持断点续传的下载软件。
世界上第一款支持断点续传的下载软件应该是GetRight。
它可让你用浏览器下载文件时有续传功能,可设定时间来下载文件或是中断Modem拨接,下载完毕时自动中断Modem拨接或关机。
为了更好的利用带宽,在断点续传的基础上,多线程下载软件逐渐发展了起来。
最早出现的多线程下载软件是中国人开发的NetAnts(网络蚂蚁)。
网络蚂蚁其实也是一个断点续传软件,但它对断点续传功能进行了扩展:可进行多点续传,即利用断点续传的原理同时建立多个连接下载同一个软件并最终将其合并为一个完整的软件。
三、P2P时代:下载再也不怕人多挤破服务器最早的P2P网络当属1979年的FidoNet(惠多网)和1984年的Usenet,经过不断发展,才有了现在人们常用的“BT”,“电驴”等P2P软件。
这类软件应用了P2P(Peer-to-Peer)技术,能够最大限度地利用网络带宽。
如今,BitComet、BitTorrent、eMule等P2P软件已经拥有极为庞大的用户群,每个人既是下载者也是上传者,一个新发布的文件转瞬之间就会像燎原之火一样遍布全世界,这是任何服务器都无法比拟的。
四、P2SP时代:多技术结合,进一步提高速度现在人们在整合了HTTP和FTP的服务器技术之后,对BT下载也进行了改进,独创了P2SP技术P2SP(Peer to Server&Peer)即点对服务器和点对点。
P2SP除了包含P2P,还多了一个“S”是指服务器。
P2SP有效地把原本孤立的服务器和其镜像资源以及P2P资源整合到了一起。
在下载的稳定性和下载的速度上,都比传统单一的P2P有了非常大的提高。
1 HTTP协议1.1HTTP协议的发展万维网WWW(World Wide Web) 之父蒂姆•贝纳斯•李早在1990年就提出了超文本传输协议HTTP(Hyper Text Transport Protocol),HTTP是WWW的基本协议,它是一个应用层的,面向对象的协议。
WWW 联盟成立后,组织了IETE ( Internet Engineering Task Force)小组进一步完善和发布HTTP 协议。
至今,HTTP协议经历了0.9、1.0、1.1三个阶段。
各阶段特征如下:1)HTTP/0.9特征:①适用于各种数据信息的简洁快速协议;②具有典型的无状态性;③无连接性;④无法使用内容协商;⑤无法显示和处理图片。
2)HTTP/1.0特征:①简单快速;②无状态性;③无连接性;④无法使用内容协商;⑤增加了元信息:在主要数据前加上一块信息,即信息的信息。
它使服务器能够提供传送数据的有关信息。
例如,传送对象是哪种类型,是用哪种语言书写的等等;⑥支持多种内容的形式,如图片、音频等。
3)HTTP /1.1特征:①持续性连接:允许请求一个web页面的浏览器发起一次连接就可从服务器上下载多个文件;②仍无状态性,但可提供状态控制;③新增加了资源请求:在原有GET、HEAD 、POST 几种方法的基础上增加了OPTIONS、PUT 、DELETE 和TRACE;④身份认证:一种简单的“提问-回答”式的基本访问授权方法。
过程是先由服务器向客户发出身份鉴别请求,再由客户发出确认信息;⑤使用内容协商机制;⑥缓存(Cache) 机制:将先前的客户请求以及请求所对应的Web 服务器响应的内容暂时保存在机器的内存或物理存储器中,使得在处理新的客户请求时可以利用。
目前新一代的HTTP协议:HTTP-NG也已经处于研究阶段,它将很有可能取代现有的HTTP,它的最大变化是是客户机可以一次连续发送多个请求,服务器依次响应每个请求。