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

标签 java android inputstream limit download-speed

我用下面的代码在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 异常。我真的不明白后台发生了什么。

为什么会出现这个问题?

最佳答案

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

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

[编辑]

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

关于java - 如何以编程方式限制下载速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52604131/

相关文章:

Java - 重复的案例标签

jax-rs - 为什么我无法访问 JAX-RS api?

java - 从 Firebase 检索一些数据并将其放在 RecyclerView 上

java - 如何使用 ZipEntry 读取位于另一个 zip 文件中的一个 zip 文件中的数据?

java - 如何模拟 javax.servlet.ServletInputStream

java - 使用按钮组使复选框不可选择

java - 将 JS 与 wicket 集成

android - React Native 获取访问 token 社交网络

android - 调试 FFmpeg

node.js - 使用express的nodejs输入流