如何以编程方式限制下载速度?

问题描述 投票:6回答:2

我使用下面的代码来限制的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();
        }
    }

}

下载速度是有限的成功,但一个新的问题出现了。当下载过程中,我从互联网上断开,下载未结束,持续了一段时间。当我断开互联网连接,它需要超过10秒抛出java.net.SocketTimeoutException例外。我真的不明白在后台发生了什么。

为什么会出现这种问题的出现?

java android inputstream limit download-speed
2个回答
2
投票

您限速不实际工作,想您所想这样做,因为数据没有实际发送的字节每字节,但在数据包。这些数据包缓冲,以及你观察(下载继续无需连接)就是你的流中读取缓冲区。一旦达到你的缓冲区的末尾,它会等待5秒超时抛出之前(因为这是你配置的内容)。

您设定的速度到8千字节/秒,和正常数据包的大小通常约为1 KB,可上升到64 KB,所以会有8秒的时间还在读同一个包。另外有可能是多个数据包中已经发送和缓冲。还存在一个接收缓冲区,this buffer can be as small as 8 - 32 kB up to several MB。所以,真正你只是从缓冲区中读取。

[编辑]

只是为了澄清,你正在做正确的事情。平均而言,速度会被限制在您指定的内容。服务器将发送一组数据,然后等待客户端已清空其缓冲区足以接收更多的数据。


3
投票

你显然是要限制在客户端的下载速度,并且还希望客户立即响应的连接被关闭。

据我所知,这是不可能的......没有一些妥协。

问题是,该客户端应用程序可以检测该连接是闭合的唯一方法是通过执行read操作。上面写着将要提供的数据。但是,如果你已经达到上限本期,那么读会推你超过限制。

这里有几个想法:

  • 如果在短期内“整合”的下载速率(例如1kbytes每一秒对每10秒10kbytes),那么你可以减少的时间长度为sleep电话。
  • 当你接近你的目标下载速率,你可以回落到做微小的(例如1个字节)读取和小睡觉。

不幸的是,这两个将在客户端(多系统调用)效率低下,但是这是如果你想你的应用程序快速检测连接关闭必须付出的成本。


在评论你说:

我期望的连接到互联网连接被禁用尽快复位。

我不认为如此。通常情况下,客户端协议堆栈将告诉它读取连接已经关闭的应用程序代码传递之前从网络接收到任何未完成的数据。

© www.soinside.com 2019 - 2024. All rights reserved.