Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java实现字节范围服务,无需仅使用Java api的servlet_Java_Http_Server_Range_Byte - Fatal编程技术网

Java实现字节范围服务,无需仅使用Java api的servlet

Java实现字节范围服务,无需仅使用Java api的servlet,java,http,server,range,byte,Java,Http,Server,Range,Byte,我用Java编写http服务器已经有相当一段时间了(现在差不多两年了?),但在处理字节范围请求时仍然遇到问题。我只使用套接字的输入和输出流进行原始字节数据传输(即文件下载/上传),使用PrintWriter向连接的客户端发送响应头/字符串(例如HTTP/1.1200 OK等等) 我不希望使用任何servlet或API(如HTTPURLConnection或其他)。我想做“香草风格” 我能够很好很快地为普通网页提供服务(比如文件浏览、上传和下载、看电影、听音乐、查看pdf文件、文本、图片、gif文

我用Java编写http服务器已经有相当一段时间了(现在差不多两年了?),但在处理字节范围请求时仍然遇到问题。我只使用套接字的输入和输出流进行原始字节数据传输(即文件下载/上传),使用
PrintWriter
向连接的客户端发送响应头/字符串(例如
HTTP/1.1200 OK
等等)

我不希望使用任何servlet或API(如HTTPURLConnection或其他)。我想做“香草风格”

我能够很好很快地为普通网页提供服务(比如文件浏览、上传和下载、看电影、听音乐、查看pdf文件、文本、图片、gif文件等),所以这不是问题

但是,每当我尝试实现字节范围请求时,服务器都会完美地接收客户端的请求,解析给定的数据,准备文件输入流以进行发送,然后当我向客户端发送数据时,客户端总是会断开与
软件导致的连接中止:套接字写入错误的连接。(我需要字节范围请求,例如:观看一小时长的视频,然后dang wifi信号消失,您不想从square one重新观看整个视频,恢复“暂停下载”等。)

这确实让我感到困惑,我确实搜索了提供字节范围请求的java示例,当我尝试实现它们时,所有这些都失败了。我甚至尝试从头开始并进行测试,结果也是一样的。以下是与我尝试完成的任务相关的代码片段:

发送和接收普通网页(工作正常,这是一个示例):

对任何感兴趣的人来说,在这个基本代码上运行的服务器在redsandbox.no-ip.org(指向我的服务器)是在线的,目前我用
Accept Ranges:none
而不是
Accept Ranges:bytes
禁用了字节请求,但我可以再次打开它进行测试

如果我需要添加更多的代码,请让我知道!谢谢你的时间。
或者,如果您希望完整查看我的服务器代码,您可以在github上查看:,因此我发现了问题所在,部分原因是。首先,我没有向客户端发送正确的
内容长度:
标题,其次,我的
endbytes
变量计算不正确。下面是e工作代码:

public static final long copyInputStreamToOutputStream(InputStream input, OutputStream output, long startBytes, long endBytes, final long contentLength, boolean debug) throws IOException {
    byte[] buffer = new byte[20480];//1024
    long count = 0;
    int read;
    long skipped = input.skip(startBytes);//I tested this quite a few times with different files and it does indeed skip the desired amount of bytes.
    final long length = (endBytes - startBytes) + 1;
    if(debug) {
        println("Start bytes: " + startBytes + "; End bytes: " + endBytes + "; Skipped bytes: " + skipped + "; Skipped equals startBytes: " + (skipped == startBytes) + "; Length: " + length + "; Total file size: " + contentLength);
    }
    long toRead = length;
    while((read = input.read(buffer)) != -1) {
        count += read;
        long bytesLeft = length - count;
        if(debug) {
            println("read: " + read + "; toRead: " + toRead + "; length: " + length + "; count: " + count + "; bytesLeft: " + bytesLeft);
        }
        toRead -= read;
        if(bytesLeft >= buffer.length) {//Changed the if statement here so that we actually send the last amount of data to the client instead of whatever is leftover in the buffer
            output.write(buffer, 0, read);
            output.flush();
        } else {
            output.write(buffer, 0, read);//Send the currently read bytes
            output.flush();
            bytesLeft = length - count;//recalculate the remaining byte count
            read = input.read(buffer, 0, (int) bytesLeft);//read the rest of the required bytes
            count += read;
            if(debug) {
                println("[Last one!]read: " + read + "; toRead: " + toRead + "; length: " + length + "; count: " + count + "; bytesLeft: " + bytesLeft);
            }
            output.write(buffer, 0, read);//and send them over
            output.flush();
            break;
        }
    }
    return count;
}

因为在我的例子中,
endBytes
变量比它需要的多了一个,所以我试图通过调整它周围的代码来进行补偿。但是,我需要简单地从中减去一个。带有
buffer.length
的if语句是为了确保最后一个字节被发送到客户端。在没有它的测试中,客户端(谷歌浏览器)挂起并等待剩余的字节,但从未收到它们,然后当30秒超时关闭连接时,网页会重置。我还没有用其他浏览器测试过这一点,但我认为它应该可以工作。

这个读写循环工作正常吗,确实看起来很精简?这是我的想法,所以可能会发现一些小的语法错误。我是应为startIdx+endIdx,两者均为包容性指标

public static final long copyInputToOutput(InputStream input, OutputStream output, long startIdx, long endIdx) throws IOException {
    final long maxread = 24*1024;
    byte[] buffer = new byte[(int)maxread];
    input.skip(startIdx);
    long written=0;
    long remaining = endIdx-startIdx+1;
    while(remaining>0) {
        int read = input.read(buffer, 0, (int)Math.min(maxread, remaining));
        output.write(buffer, 0, read);
        remaining -= read;
        written += read;
    }
    output.flush();
    return written;
}

我喜欢这样的问题,因为我也相信有可能以更有效的方式重写标准库的代码,以满足特定的需求和/或限制(特别是当您的程序必须在高负载下生存或满足高吞吐量数据流的需要时)。您是否将代码提交给某个github repo?很遗憾,没有,但如果您愿意,我可以尝试快速提交。处理所有代码可能会更容易。当然,如果您不介意使代码开源的话。Inputstream.skip(长)javadoc不能保证跳过n个字节,请检查返回值。但我认为本地fileinputstream是相当一致的。还要确保以前的stackcall函数没有使用整数,它可能会溢出。调试打印开始+结束+跳过的值以确保。顺便说一句,您的代码风格很好,理解它没有问题。我一定会尝试一下,如果行得通,我会接受的!非常好。也没有语法错误。干得好,谢谢!附言。我认为每次写后刷新(…)这样做会更好,因为它不会让客户端等待服务器准备数据,特别是对于非常大的文件。您可以在运行时刷新,但内部OS网络缓冲区在满时仍然会刷新。发生这种情况时大小未知。如果您感兴趣,下面是最终结果(添加了与swt窗口相关的代码):这是映射到Math.min(long,long)或Math.min(int,int)函数的
(int)Math.min(maxread\u int,remaining\u long)
。请小心64位长->32位int隐式转换,很难找到错误。
public static final long copyInputStreamToOutputStream(InputStream input, OutputStream output, long startBytes, long endBytes, final long contentLength, boolean debug) throws IOException {
    byte[] buffer = new byte[20480];//1024
    long count = 0;
    int read;
    long skipped = input.skip(startBytes);//I tested this quite a few times with different files and it does indeed skip the desired amount of bytes.
    final long length = (endBytes - startBytes) + 1;
    if(debug) {
        println("Start bytes: " + startBytes + "; End bytes: " + endBytes + "; Skipped bytes: " + skipped + "; Skipped equals startBytes: " + (skipped == startBytes) + "; Length: " + length + "; Total file size: " + contentLength);
    }
    long toRead = length;
    while((read = input.read(buffer)) != -1) {
        count += read;
        long bytesLeft = length - count;
        if(debug) {
            println("read: " + read + "; toRead: " + toRead + "; length: " + length + "; count: " + count + "; bytesLeft: " + bytesLeft);
        }
        toRead -= read;
        if(bytesLeft >= buffer.length) {//Changed the if statement here so that we actually send the last amount of data to the client instead of whatever is leftover in the buffer
            output.write(buffer, 0, read);
            output.flush();
        } else {
            output.write(buffer, 0, read);//Send the currently read bytes
            output.flush();
            bytesLeft = length - count;//recalculate the remaining byte count
            read = input.read(buffer, 0, (int) bytesLeft);//read the rest of the required bytes
            count += read;
            if(debug) {
                println("[Last one!]read: " + read + "; toRead: " + toRead + "; length: " + length + "; count: " + count + "; bytesLeft: " + bytesLeft);
            }
            output.write(buffer, 0, read);//and send them over
            output.flush();
            break;
        }
    }
    return count;
}
public static final long copyInputToOutput(InputStream input, OutputStream output, long startIdx, long endIdx) throws IOException {
    final long maxread = 24*1024;
    byte[] buffer = new byte[(int)maxread];
    input.skip(startIdx);
    long written=0;
    long remaining = endIdx-startIdx+1;
    while(remaining>0) {
        int read = input.read(buffer, 0, (int)Math.min(maxread, remaining));
        output.write(buffer, 0, read);
        remaining -= read;
        written += read;
    }
    output.flush();
    return written;
}