続・Java でストリーム
昨日の話の続き。
結局、BufferedInputStream で mark()/reset() を使わずにやるようにした。
HTTPヘッダは、バッファを用意して改行が連続するところまで読み込む。その後に続く Content-length 分のデータが、Content-Type で指定されたデータと見なすようにした。
private String getHeaders(BufferedInputStream in) throws IOException { byte buff = new byte[8192]; int pos = 0; while (true) { buff[pos] = read(in); if (pos > 0) { if (buff[pos - 1] == '\r' && buff[pos] == '\r') { break; } } if (pos > 2) { if (buff[pos - 3] == '\r' && buff[pos - 2] == '\n' && buff[pos - 1] == '\r' && buff[pos] == '\n') { break; } } pos++; } ByteArrayInputStream bis = new ByteArrayInputStream(buff); BufferedReader reader = new BufferedReader(new InputStreamReader(bis)); ArrayList list = new ArrayList(); while (true) { String s = reader.readLine(); if (s == null || s.length() == 0) { break; } list.add(s); } String s = new String[list.size()]; s = (String)list.toArray(s); return s; }
上のコードの最初の方にある read() は、以下のとおり。
private byte read(InputStream in) throws IOException { int b = -1; while (true) { b = in.read(); if (b >= 0) { break; } try { Thread.sleep(1); } catch (InterruptedException e) {} } return (byte)b; }
この read() は重要。1バイトのデータを読むためにループしている点に注意して欲しい。ネットワークを通して届くデータは、ストリームから read() した時点ではまだ届いていない可能性がある。ノンブロッキングモードでは、データが読めるまで read() を繰り返し呼び出さないといけない。(ネットワークプログラミングの初心者は、このことに気付かずに、データがきちんと受信できない原因について悩みつづけることが多い。)
エラーハンドリングは、呼び出し側で行う。データの終端まで読み終えたときに read() すると EOFException が発生するし、ソケットがクローズされた場合は IOException が発生するので、それらを適切に処理すればよい。