終於考完了馬原完成了大學的最後一門考試,能夠愉快的寫代碼了~今天就來記錄一下個人下載器中是如何實現限速的。java
市面上常見的專業下載器都會提供下載/上傳限速這個功能,那限速到底是限的哪個速度呢?git
咱們都知道HTTP/HTTPS的傳輸層協議是TCP,TCP是基於字節流的。字節流在Java中對應的就是InputStream
和OutputStream
。基於TCP的傳輸層下載/上傳的步驟通常是github
Socket.connect()
。這期間會經歷三次握手,是一個耗時過程,可使用鏈接複用優化(OKHttp
已經作了鏈接池緩存)。InputStream
和OutputStream
操做便可。如下載爲例,首先鏈接步驟是不須要限速的,由於鏈接部分一般是很快的,而且每每都想要鏈接更加快速。因此,咱們限速的對象確定就是從socket的InputStream
這一步驟。緩存
首先,這裏的限速並非真的限制讀寫數據流每時每刻的速度。這裏要分清高中物理學過的兩個概念——瞬時速度
和平均速度
。網絡
設爲
時刻下載的字節,
爲下載速度則有:多線程
下載的瞬時速度和不少東西有關:硬件、網絡情況、系統等,是咱們沒法控制的。 可是有一個東西咱們是,那就是
。socket
咱們下載的時候每每是在一個循環中不斷的從網絡讀取數據到內存。咱們能夠在read
以前記錄時間,
read
操做以後記錄時間。而後假設咱們想要將下載的速度限制爲
,那麼,咱們讀取這段
字節的數據指望耗時爲:post
令優化
則當時,說明當前下載速度快,咱們就能夠調用
Thread.sleep()
這段時間,使得在宏觀上下載的平均速度在咱們的限制條件內。spa
另外,若是是多線程多任務,實際上平均分配速度便可。即任務間平均分配總速度,任務內線程平均分配任務得到的速度;
時是符合限制的。
// 鏈接、獲取輸入流
...
int readSize;
long start;
do {
start = System.nanoTime();
readSize = inputStream.read(buffer, 0, buffer.length);
// targetBps是這個任務分配到的速度,targetBps < 0表示不限速
if (readSize > 0 && targetBps > 0) {
// downloadThreadCount是下載線程數
long sleepDurarion = (long) (readSize * 1000.0 / (targetBps / Math.max(downloadThreadCount, 1)) - (System.nanoTime() - start) / 1000_000.0);
if (sleepDuration > 0) {
try {
Thread.sleep(sleepDuration);
} catch (InterruptedException e) {
// do nothing
}
}
}
} while (readSize > 0);
複製代碼
上面主要是如下載爲例,事實上任何I/O流
的操做均可以經過這種方式在宏觀上「限速」。
由於這種方法限制的是平均速度,因此若是裝有網速監測軟件,可能會看到波動的網絡速度變化。
實現了限速功能的下載器項目傳送門,支持多任務、多線程、多進程、斷點續傳、速度限制等等...
其餘相關文章: