通过HTTP协议实现多线程下载
生活随笔
收集整理的這篇文章主要介紹了
通过HTTP协议实现多线程下载
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1. 基本原理,每條線程從文件不同的位置開始下載,最后合并出完整的數(shù)據(jù)。?
2. 使用多線程下載的好處?
??? 下載速度快。為什么呢?很好理解,以往我是一條線程在服務(wù)器上下載。也就是說(shuō),對(duì)應(yīng)在服務(wù)器上,有一個(gè)我的下載線程存在。?
??? 這時(shí)候肯定不只我一個(gè)人在下載,服務(wù)器上肯定同時(shí)存在多條下載線程,在下載服務(wù)器資源。對(duì)于 CPU 來(lái)說(shuō),不可能實(shí)現(xiàn)并發(fā)執(zhí)行。?
??? CPU 會(huì)公平的為這些線程劃分時(shí)間片,輪流執(zhí)行,a線程十毫秒 , b線程十毫秒...?
??? 假設(shè)運(yùn)用了本文這種手法,意味著我的下載應(yīng)用,可以同時(shí)使用服務(wù)器端的任意多條線程同時(shí)下載(理論上).?
??? 假設(shè)這個(gè)線程數(shù)目是 50 條,本應(yīng)用就將更多的得到服務(wù)器 CPU 的照顧超過(guò) 50 倍.?
??? 但是總歸會(huì)受本地網(wǎng)絡(luò)速度的限制。?
3. 每條線程要負(fù)責(zé)下載的數(shù)據(jù)長(zhǎng)度可以用 “下載數(shù)據(jù)的總長(zhǎng)度” 除以 “參與下載的線程總數(shù)” 來(lái)計(jì)算。但是要考慮到不能整除的情況。?
??? 假設(shè)有 5 條線程參與下載,那么計(jì)算公式應(yīng)該為 :?
??????????? int block = 數(shù)據(jù)總長(zhǎng)度%線程數(shù) == 0? 10/3 : 10/3+1; (不能整除,則加一)?
4. 和數(shù)據(jù)庫(kù)分頁(yè)查詢類型。每條線程需要知道自己從數(shù)據(jù)的什么位置開始下載,下載到什么位置為止。?
?? 首先,為每一個(gè)線程配備一個(gè) id , id 從零開始,為 0 1 2 3...?
?? 開始位置:線程 id 乘以每條線程負(fù)責(zé)下載的數(shù)據(jù)長(zhǎng)度.?
?? 結(jié)束位置:下一個(gè)線程開始位置的前一個(gè)位置。?
?? 如:?
????? int startPosition =? 線程id * 每條線程下載的數(shù)據(jù)長(zhǎng)度?
????? int endPosition = (線程id + 1) * 每條線程下載的數(shù)據(jù)長(zhǎng)度 -1;?
????????
5. HTTP 協(xié)議的 Range 頭可以指定從文件的什么位置開始下載,下載到什么位置結(jié)束。單位為 1byte?
?? Range:bytes=2097152-4194304 表示從文件的 2M 的位置開始下載,下載到 4M 處結(jié)束?
?? 假如 Range 指定要讀取到 文件的 5104389 的字節(jié)數(shù)位置,但是下載的文件本身只有 4104389 個(gè)長(zhǎng)度。那么下載操作自動(dòng)會(huì)在 4104389 處停止。?
?? 因此不會(huì)下載到多余的無(wú)效數(shù)據(jù).?
6. 另一個(gè)難題是如何按順序?qū)?shù)據(jù)寫往本地文件。因?yàn)?#xff0c;線程是同步執(zhí)行的,它們同時(shí)在往本地目標(biāo)文件寫入數(shù)據(jù)。
?? 而線程于線程之間寫入的數(shù)據(jù)并沒(méi)有按照下載數(shù)據(jù)本身的順序。若按照普通的 OutputStream 的寫入方式,最后的本地下載文件將失真。?
?? 于是我們將用到下面這個(gè)類:?
???? java.io.RandomAccessFile?
???? 因?yàn)榇祟愅瑫r(shí)實(shí)現(xiàn)了 DataOutput 和 DataInput 的方法。使他們同時(shí)具有寫入和讀取功能。?
???? 這個(gè)類仿佛存在一個(gè)類似文件指針的東西,可以隨意執(zhí)行文件的任意一個(gè)位置開始讀寫.?
???? 因此此類的實(shí)例支持對(duì)隨機(jī)訪問(wèn)文件的讀取和寫入.?
?????
???? 例如:?
???????
Java代碼?? ????
??? 雖然,執(zhí)行完這段代碼后,我們還沒(méi)有向目標(biāo)文件 "1.txt" 寫入任何數(shù)據(jù)。但是如果此時(shí)查看其大小,已經(jīng)為 1kb 了。這是我們自己設(shè)置的大小。?
??? 這個(gè)操作類似于向這個(gè)文件存儲(chǔ)了一個(gè)大型的 byte 數(shù)組。這個(gè)數(shù)組將這個(gè)文件撐到指定大小。等待被填滿。?
??? 既然是這樣,好處就在于,我們可以通過(guò) “索引” 隨機(jī)訪問(wèn)此文件系統(tǒng)的某個(gè)部分。?
??? 例如,可能這個(gè)文件大小為 500?
??? 那么,我的業(yè)務(wù)需求可能需要第一次 從 300 位置開始寫數(shù)據(jù),寫到 350 為止。?
??? 第二次,我又從 50 開始寫數(shù)據(jù),寫到 100 為止。?
??? 總之,我不是 “一次性” 的 “按順序” 的將這個(gè)文件寫完。?
??? 那么,RandomAccessFile 可以支持這種操作。?
?????
???? API?
????? void setLength(long newLength)?
????????? Sets the length of this file. (設(shè)置文件的預(yù)計(jì)大小)?
????? void seek(long pos)?
????????? Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.?
????????? 假設(shè)為這個(gè)方法傳入 1028 這個(gè)參數(shù),表示,將從文件的 1028 位置開始寫入。?
????? void write(byte[] b, int off, int len)?
????????? Writes len bytes from the specified byte array starting at offset off to this file.?
????? write(byte[] b)?
????????? Writes b.length bytes from the specified byte array to this file, starting at the current file pointer.?
????? void writeUTF(String str)?
????????? Writes a string to the file using modified UTF-8 encoding in a machine-independent manner.?
?????? String readLine()?
????????? Reads the next line of text from this file.?
????? 實(shí)驗(yàn)代碼:?
?????
Java代碼??
????
??? 以上實(shí)驗(yàn)成功,雖然我們寫入字符串的順序?yàn)?"2"、"1"、"3",但是因?yàn)樵O(shè)置了文件偏移量的關(guān)系,文件最終保存的數(shù)據(jù)為 : 123?
??? 另一個(gè)疑問(wèn),寫完這三個(gè)數(shù)據(jù),文件的大小已經(jīng)為 3 個(gè)字節(jié)大小了。已經(jīng)撐滿了寫入的數(shù)據(jù),那么我們繼續(xù)往里面放數(shù)據(jù)會(huì)有什么效果??
????
??? /* 向超出大小的第四個(gè)字節(jié)位置寫入數(shù)據(jù) */?
??? accessFile.seek(3);?
??? accessFile.write("400".getBytes());?
????
??? 以上代碼無(wú)論 seek 方法指定的文件指針偏移量以及存入的數(shù)據(jù),都已經(jīng)超出了最開始為文件設(shè)定的 3 個(gè)字節(jié)的大小。?
??? 按照我的猜測(cè),至少 “accessFile.seek(3)” 位置會(huì)拋出 "ArrayIndexOutOfBoundsException" 異常,表示下標(biāo)越界。?
??? 而,單獨(dú)執(zhí)行 "accessFile.write("400".getBytes())" 應(yīng)該可以成功。因?yàn)檫@個(gè)需求屬于合理的,應(yīng)該有執(zhí)行它的機(jī)制。?
??? 實(shí)驗(yàn)結(jié)果是兩句代碼都是成功的。貌似是說(shuō)明,文件隱含的大型的字節(jié)數(shù)組,可以自動(dòng)撐大。?
????
??? 但是要注意的問(wèn)題是,必須要保證所設(shè)定的文件大小的每一個(gè)位置都具有合法的數(shù)據(jù),至少不能為空。?
??? 例如:?
??????? /* 向第三個(gè)位置寫入 '3'? */?
??????? accessFile.seek(2);?
??????? accessFile.write("3".getBytes());?
????????
??????? accessFile.seek(5);?
??????? accessFile.write("400".getBytes());?
??? 那么結(jié)合之前的代碼,最后的結(jié)果為:?
??????? 123口口400?
??? 在空白的兩個(gè)位置處出現(xiàn)了亂碼。這是理所應(yīng)當(dāng)?shù)摹?
????
??? 另外,假設(shè)我們?yōu)槲募付艘话賯€(gè)長(zhǎng)度:?
??????? accessFile.setLength(100);?
??? 而,實(shí)際上,我們只為其前五個(gè)位置設(shè)置了值。那么理所當(dāng)然的是,文件保存的數(shù)據(jù),最后會(huì)綴上 95 個(gè)亂碼。?
????
7. 準(zhǔn)備工作應(yīng)該十分充分了。接下來(lái)上代碼。?
???
Java代碼?? 轉(zhuǎn)載于:https://www.cnblogs.com/bigben0123/p/4527413.html
總結(jié)
以上是生活随笔為你收集整理的通过HTTP协议实现多线程下载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 高血压的标准
- 下一篇: HDU 3932 模拟退火