web-dev-qa-db-ja.com

javaでInputStreamの終わりを識別する方法

Socketプログラムを使用してサーバーからバイトを読み取ろうとしています。つまり、InputStreamを使用してバイトを読み取っています。長さのサイズを渡すと、バイトを読み取ることができますが、長さは不明です。そのため、バイト配列を初期化できません。

また、while (in.read() != -1)を試しましたが、データが送信されるとループが正常に動作することを観察しましたが、ループの後の次の行は実行できません。ストリーム内のデータをまだ探しているようですが、データがありません。サーバー接続を閉じると、クライアントはループに続く次の行を実行します。

どこが間違っているのかわかりませんか?

this.in = socket.getInputStream();

int dataInt = this.in.read();

while(dataInt != -1){
    System.out.print(","+i+"--"+dataInt);
    i++;
    dataInt = this.in.read();
}

System.out.print("End Of loop");

出力は次のようになります:

,1--0,2--62,3--96,4--131,5--142,6--1,7--133,8--2,9--16,10--48,11--56,12--1,13--0,14--14,15--128,16--0,17--0,18--0,19--48,20--0,21--0,22--0,23--0,24--0,25--1,26--0,27--0,28--38,29--114,30--23,31--20,32--70,33--3,34--20,35--1,36--133,37--48,38--51,39--49,40--52,41--49,42--55,43--49,44--52,45--52,46--54,47--55,48--50,49--51,50--52,51--48,52--53,53--56,54--51,55--48,56--48,57--57,58--57,59--57,60--57,61--57,62--57,63--57,64--56

しかし、次の出力はありません:-ループの終わり

ループを閉じる方法を教えてください。

あなたの応答を楽しみにしています。よろしくお願いします。

37
Vardhaman

より多くのデータを探しています-そこにないより多くのデータがあることを何も告げられていないからです。ネットワークのもう一方の端は、いつでもより多くのデータを送信できます。

クライアント/サーバープロトコルを設計しているのか、それを実装しようとしているのかは明確ではありませんが、通常、メッセージの終わりを検出する3つの一般的な方法があります。

  • メッセージの最後で接続を閉じる
  • データの前にメッセージの長さを置く
  • セパレーターを使用します。通常のデータでは決して発生しない値(または何らかの形で常にエスケープされる値)

個人的には、可能であれば長さの接頭辞を優先します。読み取りコードが大幅に簡素化されますが、同じ接続で複数のメッセージを許可します。

(さらに、ダニエルと同意します。1バイトではなく、バッファ全体を一度に読み取るreadのオーバーロードを使用する必要があります。これははるかに効率的ですが、基本的には変更しません現在の問題の性質。)

49
Jon Skeet

あなたは実際にあなた自身の質問に答えたと思います。

ループを終了しないのは、サーバー側がソケットを閉じた後、クライアント側でのみ入力ストリームの終わりが発生するためです。 (より正確には、ソケット出力ストリームを閉じるか...または同等の...を閉じ、closeイベントがクライアント側に伝播した後。)

そのイベントが発生するまで、サーバーがソケットにさらにデータを書き込むことを決定する可能性があります。そのため、クライアント側の読み取りは、データをさらに取得するか、サーバーエンドが閉じられたことを示すプロトコルイベントが見つかるまでブロックします。

(実際には、他のイベントはクライアントの読み取りをブロック解除できますが、それらはすべて何らかの種類のIOExceptionになり、おそらくこれ以上データを読み取ることができないストリームになります。)


さて、後でソケットでさらに多くのものを送信するためにサーバーエンドを閉じたくない場合は、何らかの種類のフレーミングメカニズムを使用するようにアプリケーションプロトコルを変更する必要があります。たとえば、サーバーは、バイトカウントの後に指定されたバイト数で構成されるフレーム(またはレコードなど)を送信する場合があります。あるいは、識別されたバイト値またはバイトシーケンスを使用して、フレームの終わりをマークすることもできます。

17
Stephen C

この例を実行できます。

ストリームの終了を待つ場合は、EOF(-1)を受信するために送信側でストリームを閉じる必要があります。

複数のバイナリメッセージを送信する場合、コードが一度に大きなブロックを読み取ることができるため、メッセージの前に長さを送信することを好みます(つまり、取得する量がわかっているため)

ServerSocket ss = new ServerSocket(0);
Socket s = new Socket("localhost", ss.getLocalPort());
Socket s2 = ss.accept();

final OutputStream out = s.getOutputStream();
out.write("Hello World!".getBytes());
out.close();

final InputStream in = s2.getInputStream();
for (int b = 0; ((b = in.read()) >= 0);) {
    System.out.println(b + " " + (char) b);
}
System.out.println("End of stream.");
s.close();
s2.close();
ss.close();

プリント

72 H
101 e
108 l
108 l
111 o
32  
87 W
111 o
114 r
108 l
100 d
33 !
End of stream.
5
Peter Lawrey