56

Java api によると、次のInputStream.read()ように記述されています。

ストリームの終わりに達したために使用できるバイトがない場合は、値 -1 が返されます。このメソッドは、入力データが利用可能になるか、ストリームの終わりが検出されるか、例外がスローされるまでブロックされます。

読み取りを行うwhile(true)ループがあり、ストリームを介して何も送信されていない場合は常に -1 になります。それは予想されます。

私の質問は、 read() がブロックされるのはいつですか? データを取得しない場合は -1 を返します。データが受信されるまでブロッキング読み取りが待機することを期待します。入力ストリームの最後に到達した場合、read() は -1 を返すのではなく、単にデータを待つべきではありませんか?

または、ストリームにアクセスする別のスレッドがあり、read() がストリームにアクセスできない場合にのみ read() がブロックされますか?


それが私の次の質問につながります。以前は、データが利用可能になったときに通知するイベント リスナー (ライブラリによって提供される) を使用していました。通知を受けたときwhile((aByte = read()) > -1)、ストアをバイトと呼びました。非常に近い時間に 2 つのイベントを取得し、すべてのデータが表示されていないことに戸惑いました。2 番目のイベントのデータの末尾のみが表示され、残りが欠落しているように見えました。

最終的にコードを変更して、呼び出したイベントが発生したときにif(inputStream.available() > 0) while((aByte = read()) > -1)バイトを格納するようにしました。今では正常に機能し、すべてのデータが表示されました。

誰かがこの動作を説明できますか? はInputStream.available()、(ストリームの) 次の呼び出し元をブロックする前に読み取ることができるバイト数を返すと言われています。.available() を使用しない場合でも、最初のイベントの読み取りが 2 番目のイベントの読み取りをブロックするだけで、大量のストリーム データを消去したり消費したりしないと予想されます。これを行うと、すべてのデータが表示されないのはなぜですか?

4

7 に答える 7

48

の一部の実装の基になるデータ ソースInputStreamは、ストリームの終わりに到達したことを通知でき、それ以上データは送信されません。このシグナルが受信されるまで、そのようなストリームに対する読み取り操作はブロックされる可能性があります。

たとえば、ソケットInputStreamからの はSocket、FIN フラグが設定された TCP パケットを受信するまで、EOF を返すのではなくブロックします。そのようなストリームから EOF を受信すると、そのソケットで送信されたすべてのデータが確実に受信されたことが保証され、それ以上データを読み取ることができなくなります。(一方で、ブロッキング読み取りで例外が発生した場合は、一部のデータが失われた可能性があります。)

raw ファイルやシリアル ポートからのストリームなど、他のストリームには、これ以上データが利用できないことを示す同様の形式またはプロトコルがない場合があります。そのようなストリームは、現在データが利用できない場合、ブロックするのではなく、すぐに EOF (-1) を返すことができます。ただし、そのような形式やプロトコルがない場合、相手側がデータの送信をいつ完了したかを確認することはできません。


2 番目の質問に関しては、競合状態が発生したようです。問題のコードを見なくても、問題は実際には「表示」の方法にあると推測しています。おそらく、2 回目の通知までに表示しようとしたことで、最初の通知中に行われた作業が何らかの形で破壊された可能性があります。

于 2009-03-04T18:11:54.030 に答える
19

ストリームの終わりの場合は -1 を返します。ストリームがまだ開いている (つまり、ソケット接続) が、読み取り側にデータが到達していない場合 (サーバーが遅い、ネットワークが遅いなど)、read() はブロックします。

call available() は必要ありません。通知の設計を理解するのに苦労していますが、read() 自体以外の呼び出しは必要ありません。メソッド available() は便宜上のみ用意されています。

于 2009-03-04T18:09:58.900 に答える
7

デフォルトでは、提供されているRXTXInputStreamの動作は準拠していません。

受信しきい値を1に設定し、受信タイムアウトを無効にする必要があります。

serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();

ソース:RXTXシリアル接続-read()のブロックに関する問題

于 2012-04-23T15:16:17.143 に答える
4

ええ!まだストリームをあきらめないでください。ここではシリアル通信について話しています。シリアルの場合、読み取り時に -1 が返される可能性があり、返されることが絶対に予想されますが、後でデータが期待されます。問題は、TCP/IP が切断されない限り常に 0 を返す必要がある TCP/IP の扱いに慣れている人がほとんどであることです... そうです、-1 は理にかなっています。ただし、シリアルでは、長時間のデータ フローはなく、「HTTP キープ アライブ」、TCP/IP ハートビート、または (ほとんどの場合) ハードウェア フロー制御もありません。しかし、リンクは物理的であり、「銅線」で接続されており、完全に生きています。

さて、彼らの言っていることが正しければ、つまり、シリアルは -1 で閉じる必要があるのに、OnCTS、pmCarroerDetect、onDSR、onRingIndicator などを監視する必要があるのはなぜですか? 0 がそこにあることを意味する場合、および-1はそうでないことを意味し、それらすべての検出機能を台無しにします! :-)

あなたが直面している問題は、別の場所にあるかもしれません。

さて、具体的に:

Q:「2 回目のイベントのデータの最後尾だけが表示され、残りが欠落しているように見えました。」

A: 同じ byte[] バッファを再利用してループしていたと思います。最初のメッセージが入ってきて、まだ画面/ログ/標準出力に表示されていない場合 (ループにいるため)、2 番目のメッセージを読み取り、バッファー内の最初のメッセージ データを置き換えます。繰り返しますが、読んだ量を保存していないと推測するので、以前の読み取り量だけストア バッファーをオフセットするようにしました。

Q:「最終的にコードを変更して、イベントを取得したときに if(inputStream.available() > 0) while((aByte = read()) > -1) を呼び出すようにしました。」

A: ブラボー... それは良いことです。さて、データ バッファは IF ステートメント内にあり、2 番目のメッセージは 1 番目のメッセージを上書きしません。実際には、おそらく 1 番目の大きな (より大きな) メッセージでした。しかし今では、データを損なわずに、すべてを一度に読み取ることができます。

C: "...競合状態..."

A: ああ、あの古き良きキャッチ ゴート ゴート! 競合状態... :-) はい、これは競合状態であった可能性があります。実際、そうであった可能性があります。ただし、RXTX がフラグをクリアする方法にすぎない可能性もあります。「データ利用可能フラグ」のクリアは、期待したほど速くは発生しない場合があります。たとえば、データが以前に格納されていたバッファのクリアとイベントフラグの再設定に関連して、読み取りと readLine の違いを知っている人はいますか? 私もそうではありません:-) 私もまだ答えを見つけることができません... しかし... いくつかの文について、とりとめのない話をさせてください。イベント駆動型プログラミングにはまだいくつかの欠陥があります。最近対処しなければならなかった実際の例を挙げましょう。

  • TCP/IP データを取得しました。たとえば、20 バイトです。
  • したがって、受信データの OnEvent を受け取ります。
  • 20バイトでも「読み取り」を開始します。
  • 20 バイトの読み取りを完了する前に、さらに 10 バイトを取得します。
  • しかし、TCP/IP は私に通知するように見えますが、フラグがまだ設定されていることを確認し、再度通知することはありません。
  • ただし、20バイトの読み取りを終了しました(available()は20バイトあると言っていました)...
  • ... そして最後の 10 バイトは TCP/IP Q に残っています ... 通知されていないためです。

ほら、フラグがまだ設定されていたため、通知が見逃されました...バイトの読み取りを開始したにもかかわらず。バイトを終了していれば、フラグはクリアされ、次の 10 バイトの通知を受け取っていたはずです。

今あなたに起こっていることとは正反対です。

そうです、IF available() を使用してください...返されたデータの長さを読み取ります。次に、偏執的である場合は、タイマーを設定して available() を再度呼び出します。そこにまだデータがある場合は、新しいデータを読み取りません。available() が 0 (または -1) を返す場合は、リラックスして... 座って... 次の OnEvent 通知を待ちます。

于 2009-09-01T02:28:04.253 に答える
2

InputStream残念ながら、何が起こるかは実装によって決まります

何も見つからない場合:

  • ソケット(つまりSocketInputStream) は、データが受信されるまでブロックされます (デフォルト)。ただし、タイムアウトを設定することは可能です (参照: setSoTimeout)。その後、readx ミリ秒の間ブロックされます。それでも何も受信されない場合は、 aSocketTimeoutExceptionがスローされます。

    ただし、タイムアウトの有無にかかわらず、 からの読み取りSocketInputStream -1. (たとえば、複数のクライアントが同時に同じhost:portに接続すると、デバイスが接続されているように見えても、 a の結果はreadすぐに-1(データを返さない) になる可能性があります。 )

  • Serialio通信は常に返され-1ます。タイムアウトを設定することもできます (使用setTimeoutRx)。read最初に x ミリ秒ブロックされますが-1、何も見つからない場合でも結果は変わりません。(注意: ただし、複数のシリアル io クラスが利用可能です。動作はベンダーに依存する可能性があります。)

  • ファイル(リーダーまたはストリーム) はEOFException.

一般的なソリューションへの取り組み:

  • 上記のストリームのいずれかを でラップすると、 、 などのDataInputStreamメソッドを使用できます。すべての値は に変換されます。(PS: 小さな読み取りをたくさん実行する場合は、最初にラップすることをお勧めします)readBytereadChar-1EOFException BufferedInputStream
  • SocketTimeoutExceptionEOFExceptionextendsの両方がありIOException、他にもいくつかの可能な がありますIOException通信の問題を検出するために をチェックするだけで便利です。IOException

もう 1 つのデリケートなトピックはフラッシングです。flushソケットに関しては「今すぐ送信する」ことを意味しますが、Serialio に関しては「バッファを破棄する」ことを意味します。

于 2015-05-05T09:08:45.420 に答える
-7

thread.sleep() を使用すると、データストリーム全体を受信できると思います

于 2010-11-23T15:56:56.113 に答える