小碼哥

          誰謂河廣,一葦杭之

             :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            7 隨筆 :: 17 文章 :: 74 評論 :: 0 Trackbacks

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          相冊

          訂閱Canvas

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          好久沒有在這里寫點東西了,要養(yǎng)成書寫記錄的習(xí)慣。

          這里簡單的討論一下java設(shè)計網(wǎng)絡(luò)程序中如何控制上傳和下載速度,我們常見的FTP,HTTP,BT等協(xié)議都是TCP的,但是現(xiàn)在流行的utorrent卻基于UDP實現(xiàn)了自己UTP協(xié)議(UDP+擁塞控制),不管使用什么協(xié)議,站在I/O的角度來說,限速的控制思路都是一樣的。

          思路很簡單,如下:

          1.假設(shè)下載或者上傳速度上限是m (KB/s),那么發(fā)送一個固定的字節(jié)數(shù)據(jù)(假設(shè)是n字節(jié))的時間花費是:n/m;
          2.假設(shè)現(xiàn)在要發(fā)送n字節(jié)的數(shù)據(jù),那么理論所需的時間應(yīng)該是n/m,而在實際情況下,發(fā)送n字節(jié)的數(shù)據(jù)只花費了t秒,那么發(fā)送該發(fā)送線程就應(yīng)該睡眠n/m-t秒,這樣就基本實現(xiàn)了速度的控制。

          代碼以TCP為例
          速度控制
           1 package com.actiontec.net.bandwidth;
           2 
           3 /**
           4  * 
           5  * @author Le
           6  * 
           7  */
           8 public class BandwidthLimiter {
           9 
          10     /* KB */
          11     private static Long KB = 1024l;
          12 
          13     /* The smallest count chunk length in bytes */
          14     private static Long CHUNK_LENGTH = 1024l;
          15 
          16     /* How many bytes will be sent or receive */
          17     private int bytesWillBeSentOrReceive = 0;
          18 
          19     /* When the last piece was sent or receive */
          20     private long lastPieceSentOrReceiveTick = System.nanoTime();
          21 
          22     /* Default rate is 1024KB/s */
          23     private int maxRate = 1024;
          24 
          25     /* Time cost for sending CHUNK_LENGTH bytes in nanoseconds */
          26     private long timeCostPerChunk = (1000000000l * CHUNK_LENGTH)
          27             / (this.maxRate * KB);
          28 
          29     /**
          30      * Initialize a BandwidthLimiter object with a certain rate.
          31      * 
          32      * @param maxRate
          33      *            the download or upload speed in KBytes
          34      */
          35     public BandwidthLimiter(int maxRate) {
          36         this.setMaxRate(maxRate);
          37     }
          38 
          39     /**
          40      * Set the max upload or download rate in KB/s. maxRate must be grater than
          41      * 0. If maxRate is zero, it means there is no bandwidth limit.
          42      * 
          43      * @param maxRate
          44      *            If maxRate is zero, it means there is no bandwidth limit.
          45      * @throws IllegalArgumentException
          46      */
          47     public synchronized void setMaxRate(int maxRate)
          48             throws IllegalArgumentException {
          49         if (maxRate < 0) {
          50             throw new IllegalArgumentException("maxRate can not less than 0");
          51         }
          52         this.maxRate = maxRate < 0 ? 0 : maxRate;
          53         if (maxRate == 0)
          54             this.timeCostPerChunk = 0;
          55         else
          56             this.timeCostPerChunk = (1000000000l * CHUNK_LENGTH)
          57                     / (this.maxRate * KB);
          58     }
          59 
          60     /**
          61      * Next 1 byte should do bandwidth limit.
          62      */
          63     public synchronized void limitNextBytes() {
          64         this.limitNextBytes(1);
          65     }
          66 
          67     /**
          68      * Next len bytes should do bandwidth limit
          69      * 
          70      * @param len
          71      */
          72     public synchronized void limitNextBytes(int len) {
          73         this.bytesWillBeSentOrReceive += len;
          74 
          75         /* We have sent CHUNK_LENGTH bytes */
          76         while (this.bytesWillBeSentOrReceive > CHUNK_LENGTH) {
          77             long nowTick = System.nanoTime();
          78             long missedTime = this.timeCostPerChunk
          79                     - (nowTick - this.lastPieceSentOrReceiveTick);
          80             if (missedTime > 0) {
          81                 try {
          82                     Thread.sleep(missedTime / 1000000,
          83                             (int) (missedTime % 1000000));
          84                 } catch (InterruptedException e) {
          85                     e.printStackTrace();
          86                 }
          87             }
          88             this.bytesWillBeSentOrReceive -= CHUNK_LENGTH;
          89             this.lastPieceSentOrReceiveTick = nowTick
          90                     + (missedTime > 0 ? missedTime : 0);
          91         }
          92     }
          93 }
          94 

          下載控制
           1 package com.actiontec.net.bandwidth;
           2 
           3 import java.io.IOException;
           4 import java.io.InputStream;
           5 
           6 /**
           7  * @author Le
           8  *
           9  */
          10 public class DownloadLimiter extends InputStream {
          11     private InputStream is = null;
          12     private BandwidthLimiter bandwidthLimiter = null;
          13     
          14     public DownloadLimiter(InputStream is, BandwidthLimiter bandwidthLimiter)
          15     {
          16         this.is = is;
          17         this.bandwidthLimiter = bandwidthLimiter;
          18     }
          19     @Override
          20     public int read() throws IOException {
          21         if(this.bandwidthLimiter != null)
          22             this.bandwidthLimiter.limitNextBytes();
          23         return this.is.read();
          24     }
          25 
          26     public int read(byte b[], int off, int len) throws IOException
          27     {
          28         if (bandwidthLimiter != null)
          29             bandwidthLimiter.limitNextBytes(len);
          30         return this.is.read(b, off, len);
          31     }
          32 }

          同樣,上傳控制

           1 package com.actiontec.net.bandwidth;
           2 
           3 import java.io.IOException;
           4 import java.io.OutputStream;
           5 
           6 /**
           7  * @author Le
           8  *
           9  */
          10 public class UploadLimiter extends OutputStream {
          11     private OutputStream os = null;
          12     private BandwidthLimiter bandwidthLimiter = null;
          13     
          14     public UploadLimiter(OutputStream os, BandwidthLimiter bandwidthLimiter)
          15     {
          16         this.os = os;
          17         this.bandwidthLimiter = bandwidthLimiter;
          18     }
          19     
          20     @Override
          21     public void write(int b) throws IOException {
          22         if (bandwidthLimiter != null)
          23             bandwidthLimiter.limitNextBytes();
          24         this.os.write(b);
          25     }
          26     
          27     public void write(byte[] b, int off, int len) throws IOException {
          28         if (bandwidthLimiter != null)
          29             bandwidthLimiter.limitNextBytes(len);
          30         this.os.write(b, off, len);
          31     }
          32 
          33 }

          對于一個TCP socket

          1 ServerSocket socket = new ServerSocket();
          2 //其它初始化略

           1 //從socket中以一定的速率讀數(shù)據(jù)
           2 //```java
           3 DownloadLimiter dl = new DownloadLimiter(socket.getInputStream(), new BandwidthLimiter(6250));
           4 is = new DataInputStream(dl);
           5 
           6 //讀數(shù)據(jù)
           7 int len = is.readInt();
           8 ByteBuffer buffer = ByteBuffer.allocate(4 + len);
           9 buffer.putInt(len);
          10 is.readFully(buffer.array(), 4, buffer.remaining());
          11 //```
          12 
          13 //以一定的速率寫數(shù)據(jù)到socket
          14 //```java
          15 UploadLimiter ul = new UploadLimiter(socket.getOutputStream(), new BandwidthLimiter(6250));
          16 ul.write();
          17 //```

          在多線程環(huán)境下也可以使用上述的方法。最后附圖是任務(wù)管理器的網(wǎng)絡(luò)利用率圖6250KB/s(也就是50000kb/s,附圖中網(wǎng)絡(luò)利用率也在5%左右,所以應(yīng)該這個做法還算準(zhǔn)確)
          posted on 2012-10-18 16:34 小碼哥 閱讀(12964) 評論(2)  編輯  收藏 所屬分類: Java語言學(xué)習(xí)

          評論

          # re: Java程序如何限速(控制下載和上傳速度) 2012-11-09 10:52 chwj
          我調(diào)整new BandwidthLimiter(6250)為其他數(shù)值后(除0外),測試下載均顯示利用率為0.35左右(100Mbps),而服務(wù)器端則顯示發(fā)送在2.0~1.8MBps浮動。  回復(fù)  更多評論
            

          # re: Java程序如何限速(控制下載和上傳速度) 2012-11-10 00:03 小碼哥
          @chwj
          我在使用過程中也發(fā)現(xiàn)不準(zhǔn),根據(jù)每次發(fā)送數(shù)據(jù)大小(大小基本一致比較好)適當(dāng)調(diào)整CHUNK_LENGTH大小,可能會有些效果。單線程情況下會準(zhǔn)一些。暫時還沒有想到其它好的方法。  回復(fù)  更多評論
            

          主站蜘蛛池模板: 友谊县| 衡水市| 墨竹工卡县| 瑞安市| 高尔夫| 清流县| 昌平区| 云和县| 余干县| 泸西县| 牟定县| 新乡县| 台山市| 广昌县| 介休市| 谢通门县| 汉川市| 和顺县| 堆龙德庆县| 新丰县| 鲜城| 谷城县| 延吉市| 兴业县| 武城县| 江北区| 紫云| 兰西县| 咸阳市| 灵丘县| 尉氏县| 竹溪县| 龙里县| 榆中县| 巴林左旗| 西平县| 广饶县| 洞头县| 青冈县| 江城| 年辖:市辖区|