読み取り対象として Selector に登録された SocketChannel (ノンブロッキング モード) があるとします。select() セレクターが、このチャネルが読み取りの準備ができており、ByteBuffer があることを通知したとします。チャネルからこのバッファにいくつかのバイトを読み取ります (ByteBuffer は読み取り前にクリアされます)。このために、読み取った実際のバイト数を返すチャネルの read() メソッドを使用します。チャネルから読み取った後、この数値が正であり、ByteBuffer のメソッド hasRemaining() が true を返したとします。この状況で、すぐに同じチャネルからもう少し読み込もうとするのは実用的ですか? write() についても同じ質問です。write() が正の値を返し、バッファのすべての内容が送信されたわけではない場合、write() がゼロを返すまですぐに再試行することは実用的ですか?
2 に答える
それはすべて、データが到着するデータ レートと、アプリケーションのレイテンシ要件によって異なります。待ち時間をまったく気にしない場合は、バッファーを満たすのに十分なデータが到着したと思われるまで読み取りの関心を遅らせることで、わずかに高い帯域幅を得ることができます。
ただし、注意が必要です。読み取りを遅らせると、カーネルがより多くのデータをバッファリングし、おそらくそのバッファがいっぱいになり、パケットのドロップを開始するか、何らかのフロー制御を実行する必要が生じる可能性があります。それは最後の段落からの利益を殺す以上のものです.
したがって、一般的に、できるだけ早く、できるだけ多くのことを読みたいと考えています。読み取りをバッチ処理する利点はせいぜいマイナーであり、潜在的な落とし穴は大きなものになる可能性があります。また、完全でない読み取りが見られるという事実は、データが入ってくるよりも速く処理されていることを意味することに注意してください。小さな読み取りのオーバーヘッドは基本的に無料です。
短い読み取り結果が得られた場合、ブロックせずに読み取るデータはこれ以上ないため、ブロックされるまで再度読み取ってはなりません。そうしないと、次の読み取りでほぼ確実に 0 または -1 が返されます。
読み取りがバッファーをいっぱいにする場合、その 1 つの接続の観点からは、<= 0 が返されるまで読み取りを続けることは理にかなっていますが、他のチャネルからサイクルを盗んでいます。公平性も考慮する必要があります。一般に、おそらく 1 回の読み取りを行い、選択したキーを繰り返し処理する必要があります。そこにさらにデータがある場合、select は次回に通知します。
大きなバッファを使用します。
これは、各読み取りの前にバッファーをクリアするのが間違っていることも意味します。フリップ/取得/圧縮サイクルでデータを取得する必要があります。その後、バッファーは再び読み取る準備ができており、データを失うリスクはありません。これは、接続ごとにバッファが必要であることを意味します。