代码之家  ›  专栏  ›  技术社区  ›  Hadi

如何通过编程限制下载速度?

  •  6
  • Hadi  · 技术社区  · 6 年前

    我使用以下代码限制java文件的下载速度:

    package org;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    class MainClass {
    
        public static void main(String[] args) {
            download("https://speed.hetzner.de/100MB.bin");
        }
    
        public static void download(String link) {
            try {
                URL url = new URL(link);
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(5000);
                con.setReadTimeout(5000);
                InputStream is = con.getInputStream();
                CustomInputStream inputStream = new CustomInputStream(is);
                byte[] buffer = new byte[2024];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    System.out.println("downloaded : " + len);
                    //save file
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        public static class CustomInputStream extends InputStream {
    
            private static final int MAX_SPEED = 8 * 1024;
            private final long ONE_SECOND = 1000;
            private long downloadedWhithinOneSecond = 0L;
            private long lastTime = System.currentTimeMillis();
    
            private InputStream inputStream;
    
            public CustomInputStream(InputStream inputStream) {
                this.inputStream = inputStream;
                lastTime = System.currentTimeMillis();
            }
    
            @Override
            public int read() throws IOException {
                long currentTime;
                if (downloadedWhithinOneSecond >= MAX_SPEED
                        && (((currentTime = System.currentTimeMillis()) - lastTime) < ONE_SECOND)) {
                    try {
                        Thread.sleep(ONE_SECOND - (currentTime - lastTime));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    downloadedWhithinOneSecond = 0;
                    lastTime = System.currentTimeMillis();
                }
                int res = inputStream.read();
                if (res >= 0) {
                    downloadedWhithinOneSecond++;
                }
                return res;
            }
    
            @Override
            public int available() throws IOException {
                return inputStream.available();
            }
    
            @Override
            public void close() throws IOException {
                inputStream.close();
            }
        }
    
    }
    

    下载速度被成功地限制了,但是一个新的问题出现了。当下载正在进行时,我断开了与internet的连接,下载不会结束并持续一段时间。当我断开互联网连接时,抛出一个 java.net.SocketTimeoutException 例外。我真的不明白后台发生了什么。

    为什么会出现这个问题?

    2 回复  |  直到 5 年前
        1
  •  3
  •   Stephen C    6 年前

    显然,您希望限制客户端的下载速度,并且您还希望客户端做出响应 立即 连接正在关闭。

    好吧,这不可能。。。没有任何妥协。

    问题是 只有 read 操作。读取将传递数据。但是如果你已经达到了当前时期的极限,那么这个读数会把你推到极限之上。

    以下是一些想法:

    • sleep 电话。

    • 当你接近你的目标下载速率时,你可以退回到进行微小的(例如1字节)读取和小的睡眠。

    不幸的是,这两种方法在客户端都是低效的(更多的系统调用),但是如果您想让应用程序快速检测连接关闭,就必须付出这样的代价。


    你在评论中说:

    我不这么认为。通常,客户端协议栈会在通知应用程序代码它正在读取的连接已经关闭之前,传递从网络接收到的任何未完成的数据。

        2
  •  2
  •   kutschkem    6 年前

    您的速率限制实际上并不像您想象的那样工作,因为数据实际上不是按字节发送的,而是以数据包的形式发送的。这些数据包是缓冲的,而您观察到的(下载在没有连接的情况下继续)只是您的流读取缓冲区。一旦到达缓冲区的末尾,它将等待5秒,然后抛出超时(因为这是您配置的)。

    您将速率设置为8kb/s,并且正常的数据包大小通常在1kb左右,可以达到64kb,因此您仍在读取同一数据包的时间为8秒。此外,可能已经发送并缓冲了多个数据包。还有一个接收缓冲区, this buffer can be as small as 8 - 32 kB up to several MB . 所以实际上你只是在从缓冲区读取。

    [编辑]

    . 平均而言,费率将限制在您指定的范围内。服务器将发送一组数据,然后等待客户机清空其缓冲区以接收更多数据。

    推荐文章