【发布时间】:2016-01-15 20:51:28
【问题描述】:
我已经用 Java 编写了一个 http 服务器已经有一段时间了(现在快两年了?),但我仍然在让字节范围请求工作时遇到问题。我只使用套接字的输入和输出流进行原始字节数据传输(即文件下载/上传),并使用PrintWriter 向连接的客户端发送响应头/字符串(例如HTTP/1.1 200 OK 等)。
我不希望使用任何 servlet 或 API(例如 HTTPURLConnection 或其他)。我想做“香草风格”。
我能够很好地和快速地提供普通网页(例如文件浏览、上传和下载、看电影、听音乐、查看 pdf 文件、文本、图片、gif 文件等),所以是不是问题。
但是,每当我尝试实现字节范围请求时,服务器都会完美接收客户端的请求,解析给定的数据,准备文件输入流以进行发送,然后当我将数据发送给客户端时,客户端总是丢弃与software caused connection abort: socket write error 的连接。(对于以下情况,我需要字节范围请求:观看一小时长的视频,然后该当的 wifi 信号消失,您不想从第一格重新观看整个视频,恢复一个 '暂停下载”等)
这真的让我很困惑,而且我确实搜索过服务字节范围请求的 java 示例,当我尝试实现它们时所有这些都失败了。我什至尝试从头开始并对其进行测试,并且产生了相同的结果。以下是与我要完成的工作相关的代码 sn-ps:
发送和接收普通网页(工作正常,这是一个例子):
...
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.no-ip.org(指向我的服务器),目前我使用 Accept-Ranges: none 而不是 @987654328 禁用了字节请求@,但我可以再次打开它来测试它。
如果我需要添加更多代码,请告诉我!感谢您的时间。 或者,如果您想查看我服务器的完整代码,可以在 github 上查看:https://github.com/br45entei/JavaWebServer
【问题讨论】:
-
我喜欢这样的问题,因为我也相信可以以更有效的方式重写标准库的代码以满足特定的要求和/或限制列表(尤其是当您的程序必须在高负载下生存或满足高吞吐量数据流的需求)。
-
你的代码有提交到某个 github repo 吗?
-
很遗憾,没有,但如果您愿意,我可以尝试快速完成。
-
处理所有代码可能更容易。当然,如果你不介意让你的代码开源。
-
Inputstream.skip(long) 不保证 javadoc 跳过 n 个字节,检查返回值。但我认为本地文件输入流是相当一致的。还要确保以前的堆栈调用函数都没有使用整数,它可能会溢出。调试打印开始+结束+跳过的值以确保。顺便说一句,您的代码风格很好,理解它没有问题。
标签: java http server range byte