http Java实现字节范围服务,而不使用仅使用Java api的servlet
我用Java编写http服务器已经有一段时间了(差不多两年了?)我仍然在处理字节范围请求时遇到问题。我只使用socket的输入和输出流进行原始字节数据传输(即文件下载/上传),使用PrintWriter
向连接的客户端发送响应头/字符串(例如HTTP/1.1 200 OK
等等)
我不希望使用任何servlet或API(如HTTPURLConnection或其他)。我想做“香草风格”
我能够很好很快地为普通网页提供服务(比如文件浏览、上传和下载、看电影、听音乐、查看pdf文件、文本、图片、gif文件等),所以这不是问题
但是,每当我尝试实现字节范围请求时,服务器都会完美地接收客户机的请求,解析给定的数据,准备文件输入流以便发送,然后当我向客户机发送数据时,客户机总是断开与software caused connection abort: socket write error
的连接。(我需要字节范围请求,例如:观看一小时长的视频,然后dang wifi信号消失,您不想从square one重新观看整个视频,恢复“暂停下载”等。)
这确实让我感到困惑,我确实搜索了提供字节范围请求的java示例,当我尝试实现它们时,所有这些都失败了。我甚至尝试从头开始测试,结果也一样。以下是与我试图实现的目标相关的代码片段:
发送和接收普通网页(工作正常,这是一个示例):
...
OutputStream outStream = client.getOutputStream();
PrintWriter out = new PrintWriter(new OutputStreamWriter(outStream, StandardCharsets.UTF_8), true);
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: text/html; charset=\"UTF-8\"");
String responseStr = "<!DOCTYPE html><html><head><title>Hello, world!</title></head><body><string>This is a simple example webpage!</string></body></html>";
if(acceptEncoding.contains("gzip") && responseStr.length() > 33) {
out.println("Content-Encoding: gzip");
byte[] r = StringUtil.compressString(responseStr, "UTF-8");
out.println("Content-Length: " + r.length);
out.println("");
if(protocol.equalsIgnoreCase("GET")) {
outStream.write(r);
}
} else {
out.println("Content-Length: " + responseStr.length());
out.println("");
if(request.protocol.equalsIgnoreCase("GET")) {
out.println(responseStr);
}
}
out.flush();
//followed by closing resources, etc.
...
字节范围服务(解析客户端请求数据后):
public static final long copyInputStreamToOutputStream(InputStream input, OutputStream output, long startBytes, long endBytes) throws IOException {
byte[] buffer = new byte[20480];//1024
long count = 0;
int read;
input.skip(startBytes);
long toRead = (endBytes - startBytes) + 1;
while((read = input.read(buffer)) > 0) {
if((toRead -= read) > 0) {
output.write(buffer, 0, read);//Socket error happens on this line mostly
output.flush();
} else {
output.write(buffer, 0, (int) toRead + read);//but also on this line sometimes
output.flush();
break;
}
count += read;
}
return count;
}
对于任何感兴趣的人来说,运行此基本代码的服务器都可以在redsandbox上联机。没有ip。org(指向我的服务器),目前我用Accept-Ranges: none
而不是Accept-Ranges: bytes
禁用了字节请求,但我可以再次打开它来测试它
如果我需要添加更多的代码,请让我知道!谢谢你抽出时间。 或者,如果您希望完整查看我的服务器代码,可以在github上查看:https://github.com/br45entei/JavaWebServer
# 1 楼答案
所以我明白了我的问题所在,部分原因是Whome。首先,我没有向客户端发送正确的
Content-Length:
头,其次,我的endbytes
变量计算错误。以下是工作代码:因为在我的例子中
endBytes
变量比需要的多了一个,所以我试图通过调整它周围的代码来进行补偿。然而,我只需要从中减去一个。带有buffer.length
的if语句用于确保将最后一个字节发送到客户端。在我没有它的测试中,客户端(google chrome)挂起并等待剩余的字节,但从未收到它们,然后当30秒超时关闭连接时,网页重置。我还没有在其他浏览器上进行测试,但我认为它应该可以工作# 2 楼答案
这个读写循环可以正常工作吗?当然看起来是流线型的?这是我脑子里想不出来的,所以可能会发现一些小的语法错误。我期待startIdx+endIdx都是包容性指标