小碼哥

          誰謂河廣,一葦杭之

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

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          相冊

          訂閱Canvas

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          好久沒有在這里寫點東西了,要養成書寫記錄的習慣。

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

          思路很簡單,如下:

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

          代碼以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中以一定的速率讀數據
           2 //```java
           3 DownloadLimiter dl = new DownloadLimiter(socket.getInputStream(), new BandwidthLimiter(6250));
           4 is = new DataInputStream(dl);
           5 
           6 //讀數據
           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 //以一定的速率寫數據到socket
          14 //```java
          15 UploadLimiter ul = new UploadLimiter(socket.getOutputStream(), new BandwidthLimiter(6250));
          16 ul.write();
          17 //```

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

          評論

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

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

          主站蜘蛛池模板: 奎屯市| 白沙| 仁布县| 龙里县| 德昌县| 吴桥县| 鄂伦春自治旗| 大城县| 和龙市| 湘潭市| 亚东县| 安陆市| 武陟县| 辉南县| 固阳县| 眉山市| 蒙阴县| 集贤县| 静乐县| 长武县| 临朐县| 贺兰县| 温泉县| 武平县| 甘孜| 房产| 潜山县| 延长县| 庆阳市| 讷河市| 林甸县| 洞头县| 内黄县| 松潘县| 保山市| 佛教| 广河县| 射洪县| 墨玉县| 茶陵县| 山东省|