私はこのために単純なシングルスレッドのWebサーバーを作成しました。それは非常に簡単だったことが判明しました。サーバーはかなり簡単です。コードは少し荒いですが、主なアイデアはそこにあります。
これは、最初のチャンクとしてfilecontentsを送信し、フッターとしてファイルのチェックサムを送信します。
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
public class ChunkedResponseServer implements Runnable {
private static final Logger LOGGER = Logger.getLogger(ChunkedResponseServer.class);
// Space ' '
static final byte SP = 32;
// Tab ' '
static final byte HT = 9;
// Carriage return
static final byte CR = 13;
// Line feed character
static final byte LF = 10;
final int port;
private volatile boolean cancelled = false;
public ChunkedResponseServer(int port) {
LOGGER.info("Chunked response server running on port " + port);
this.port = port;
}
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
while (!cancelled) {
final Socket connectionSocket = serverSocket.accept();
handle(connectionSocket);
}
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
public void cancel() {
LOGGER.info("Shutting down Chunked response Server");
cancelled = true;
}
private void handle(Socket socket) throws IOException {
BufferedReader input = null;
DataOutputStream output = null;
try {
input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
output = new DataOutputStream(socket.getOutputStream());
addHeaders(output);
addCRLR(output);
final String filename = readFilename(input);
final byte[] content = readContent(filename);
addContentAsChunk(output, content);
final String checksum = DigestUtils.md5Hex(content);
addLastChunkAndChecksumFooter(output, checksum);
addCRLR(output);
} finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
}
}
private void addLastChunkAndChecksumFooter(DataOutputStream output, String checksum) throws IOException {
output.writeBytes("0");
addCRLR(output);
output.writeBytes("checksum: " + checksum);
addCRLR(output);
}
private void addContentAsChunk(DataOutputStream output, byte[] content) throws IOException {
output.writeBytes(Integer.toHexString(content.length));
addCRLR(output);
output.write(content);
addCRLR(output);
}
private void addCRLR(DataOutputStream output) throws IOException {
output.writeByte(CR);
output.writeByte(LF);
}
private void addHeaders(DataOutputStream output) throws IOException {
output.writeBytes("HTTP/1.1 200 OK");
addCRLR(output);
output.writeBytes("Content-type: text/plain");
addCRLR(output);
output.writeBytes("Transfer-encoding: chunked");
addCRLR(output);
output.writeBytes("Trailer: checksum");
addCRLR(output);
}
private String readFilename(BufferedReader input) throws IOException {
final String initialLine = input.readLine();
final String filePath = initialLine.split(" ")[1];
final String[] components = filePath.split("/");
return components[components.length - 1];
}
private byte[] readContent(String filename) throws IOException {
final InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
return IOUtils.toByteArray(in);
}
}
これはクライアントが本当に必要ですか? 'TE'と' Trailer'は非常にまれにしか使われていませんが、実際には実際のコードでは見たことがありません。サーブレットAPIは、チャンクされたレスポンスをサポートしています(さらに、レスポンスのコンテンツ長を設定しないと、デフォルトでそれが送信されます)。しかし、チャンクされたレスポンスの予告編のサポートは組み込まれていません。代わりに、希望のトレーラの値をカスタムレスポンスヘッダーとして設定することもできます(値がUS-ASCIIと互換性があり、特定の最大長を超えない場合のみ)。 – BalusC
@BalusC私の使用例は、サーバーが無限のデータストリームを送信していて、コンテンツの長さを知らないことです。サーバコードは、そのチェックサムをその場で計算して、トレーラとして送信することもできます。このトレーラは、クライアントがデータの検証に使用します。 –