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为例
速度控制
public class BandwidthLimiter {/* KB */private static Long KB = 1024l;/* The smallest count chunk length in bytes */private static Long CHUNK_LENGTH = 1024l;/* How many bytes will be sent or receive */private int bytesWillBeSentOrReceive = 0;/* When the last piece was sent or receive */private long lastPieceSentOrReceiveTick = System.nanoTime();/* Default rate is 1024KB/s */private int maxRate = 1024;/* Time cost for sending CHUNK_LENGTH bytes in nanoseconds */private long timeCostPerChunk = (1000000000l * CHUNK_LENGTH)/ (this.maxRate * KB);/*** Initialize a BandwidthLimiter object with a certain rate.** @param maxRate* the download or upload speed in KBytes*/public BandwidthLimiter(int maxRate) {this.setMaxRate(maxRate);}/*** Set the max upload or download rate in KB/s. maxRate must be grater than* 0. If maxRate is zero, it means there is no bandwidth limit.** @param maxRate* If maxRate is zero, it means there is no bandwidth limit.* @throws IllegalArgumentException*/public synchronized void setMaxRate(int maxRate)throws IllegalArgumentException {if (maxRate < 0) {throw new IllegalArgumentException("maxRate can not less than 0");}this.maxRate = maxRate < 0 ? 0 : maxRate;if (maxRate == 0)this.timeCostPerChunk = 0;elsethis.timeCostPerChunk = (1000000000l * CHUNK_LENGTH)/ (this.maxRate * KB);}/*** Next 1 byte should do bandwidth limit.*/public synchronized void limitNextBytes() {this.limitNextBytes(1);}/*** Next len bytes should do bandwidth limit** @param len*/public synchronized void limitNextBytes(int len) {this.bytesWillBeSentOrReceive += len;/* We have sent CHUNK_LENGTH bytes */while (this.bytesWillBeSentOrReceive > CHUNK_LENGTH) {long nowTick = System.nanoTime();long missedTime = this.timeCostPerChunk- (nowTick - this.lastPieceSentOrReceiveTick);if (missedTime > 0) {try {Thread.sleep(missedTime / 1000000,(int) (missedTime % 1000000));} catch (InterruptedException e) {e.printStackTrace();}}this.bytesWillBeSentOrReceive -= CHUNK_LENGTH;this.lastPieceSentOrReceiveTick = nowTick+ (missedTime > 0 ? missedTime : 0);}}}
下载控制
import java.io.IOException;import java.io.InputStream;public class DownloadLimiter extends InputStream {private InputStream is = null;private BandwidthLimiter bandwidthLimiter = null;public DownloadLimiter(InputStream is, BandwidthLimiter bandwidthLimiter){this.is = is;this.bandwidthLimiter = bandwidthLimiter;}@Overridepublic int read() throws IOException {if(this.bandwidthLimiter != null)this.bandwidthLimiter.limitNextBytes();return this.is.read();}public int read(byte b[], int off, int len) throws IOException{if (bandwidthLimiter != null)bandwidthLimiter.limitNextBytes(len);return this.is.read(b, off, len);}}
上传控制
import java.io.IOException;import java.io.OutputStream;public class UploadLimiter extends OutputStream {private OutputStream os = null;private BandwidthLimiter bandwidthLimiter = null;public UploadLimiter(OutputStream os, BandwidthLimiter bandwidthLimiter){this.os = os;this.bandwidthLimiter = bandwidthLimiter;}@Overridepublic void write(int b) throws IOException {if (bandwidthLimiter != null)bandwidthLimiter.limitNextBytes();this.os.write(b);}public void write(byte[] b, int off, int len) throws IOException {if (bandwidthLimiter != null)bandwidthLimiter.limitNextBytes(len);this.os.write(b, off, len);}}
对于一个TCP socket
ServerSocket socket = new ServerSocket();//其它初始化略//从socket中以一定的速率读数据DownloadLimiter dl = new DownloadLimiter(socket.getInputStream(), new BandwidthLimiter(6250));is = new DataInputStream(dl);//读数据int len = is.readInt();ByteBuffer buffer = ByteBuffer.allocate(4 + len);buffer.putInt(len);is.readFully(buffer.array(), 4, buffer.remaining());//以一定的速率写数据到socketUploadLimiter ul = new UploadLimiter(socket.getOutputStream(), new BandwidthLimiter(6250));ul.write();
在多线程环境下也可以使用上述的方法。最后附图是任务管理器的网络利用率图6250KB/s(也就是50000kb/s,附图中网络利用率也在5%左右,所以应该这个做法还算准确)
