【问题标题】:How to programmatically limit the download speed?如何以编程方式限制下载速度?
【发布时间】:2018-10-02 07:55:46
【问题描述】:

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

}

成功限制下载速度,但出现了新问题。当下载正在进行时,我断开与互联网的连接,下载并没有结束并继续一段时间。当我断开互联网连接时,抛出java.net.SocketTimeoutException 异常需要超过 10 秒。我真的不明白后台发生了什么。

为什么会出现这个问题?

【问题讨论】:

标签: java android inputstream limit download-speed


【解决方案1】:

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

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

[编辑]

澄清一下,你做的是对的。平均而言,费率将限制在您指定的范围内。服务器会发送一堆数据,然后等到客户端清空缓冲区以接收更多数据。

【讨论】:

  • @HarryCoder 是也不是。渐进式下载的缓冲区是应用程序缓冲区。因此,如果您不限制速率,而是将数据写入应用程序内的(更大)缓冲区,该缓冲区是从不同的线程读取的。但这个想法是相似的 - 更好地匹配生产者速度与消费者速度。
  • @cse 什么,这完全是对“为什么在我断开互联网连接后我的下载持续超过 10 秒”这个问题的答案
  • @cse 好吧,OP 所做的是正确的,只是他不能指望不会有任何缓冲。平均而言,费率将限制在 OP 指定的范围内。
  • @cse 编辑了答案以澄清。
  • 我希望在禁用互联网连接后立即重置;这应该会在下一次调用读取时导致 IOException。显然,连接不会被系统重置,可能是为了更优雅地处理间歇性连接问题。
【解决方案2】:

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

AFAIK,这是不可能的......没有一些妥协。

问题在于,客户端应用程序可以检测到连接已关闭的唯一方法是执行read 操作。该读取将传递数据。但是,如果您已经达到当前期间的限制,那么该读取将推动您超过限制。

这里有几个想法:

  • 如果您在短时间内“整合”下载速率(例如每秒 1kbytes 与每 10 秒 10kbytes),那么您可以减少sleep 调用的时间长度。

    李>
  • 当您接近目标下载速率时,您可以回退到执行微小(例如 1 字节)读取和少量休眠。

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


在你说的评论中:

我希望在禁用 Internet 连接后立即重置连接。

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

【讨论】:

  • 当然,我们怎么会知道连接已断开,而且响应时间比正常情况要长?
  • 这是另一个问题。您可以确定的唯一方法是 remote 端是否关闭/重置连接。 (如果您的机器失去与本地网络的连接等,它可能会重新建立......而不会中断与远程的连接。)
  • 1) 对不起。 No. 2) No. 3) 搜索ADM源码看看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 2016-02-03
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多