ノンブロッキングIOをバッファリングするにはどうすればよいですか?
-
10-07-2019 - |
質問
ファイル記述子のブロックでバッファIOが必要な場合、stdioを使用します。しかし、手動stdioバッファリングに従ってファイル記述子を非ブロッキングモードにすると、使用できなくなります。いくつかの調査の後、BIOは非ブロッキングIOのバッファリングに使用できることがわかりました。
しかし、他の選択肢がありますか?
マルチ接続環境でスレッドの使用を避けるためにこれが必要です。
解決
あなたが話しているのは、リアクタパターンだと思います。これは、スレッドなしで多くのネットワーク接続を処理するかなり標準的な方法であり、マルチプレイヤーゲームサーバーエンジンでは非常に一般的です。別の実装(Python)は、ねじれたマトリックスです。
基本的なアルゴリズムは次のとおりです。
- 各ソケットにバッファを用意します
- 読み込む準備ができているソケットを確認します(select()、poll()、または単に反復)
- 各ソケット:
- recv()を呼び出し、recvが0またはEWOULDBLOCKのエラーを返すまで、内容をソケットのバッファーに蓄積します
- バッファのコンテンツを使用して、ソケットのアプリケーションレベルのデータハンドラを呼び出す
- ソケットのバッファをクリア
他のヒント
質問は現在編集されており、少なくとも以前よりも理解しやすくなっています。
とにかく、これは矛盾ではありませんか?
- I / Oを非ブロッキングにすると、少量のデータをすばやく読み取ることができるようになります。通常、待ち時間のスループットが犠牲になります。
- レイテンシーはそれほど気にしないのでバッファリングしますが、レイテンシーをスループットと引き換えにI / Oサブシステムを効率的に使用したいです。
両方を同時に行うことは矛盾のように思われ、想像するのは困難です。
あなたが求めているセマンティクスは何ですか?これを行う場合:
int fd;
char buf[1024];
ssize_t got;
fd = setup_non_blocking_io(...);
got = read(fd, buf, sizeof buf);
3バイトの空きがある場合、どのような動作を期待しますか?ブロッキング/バッファI / Oは、より多くの読み取りが要求を満たすまでブロックされる可能性があり、非ブロッキングI / Oは使用可能な3バイトをすぐに返します。
もちろん、「このI / Oが不完全であり、さらにデータがあるまで解析できない」ことを知ることができるように、何らかのメッセージ構造を定義するプロトコルを上部に持っている場合は、そのレベルで自分でバッファリングでき、完全なメッセージが受信されるまでデータを上に渡さない。
プロトコルによっては、非ブロッキングネットワークノード(クライアントまたはサーバー)の読み取りをバッファリングする必要がある可能性があります。
通常、これらのバッファーは、処理された最後のバイトの位置と読み取られた最後のバイトの両方を記録する複数のインデックス(オフセット)を提供します(処理されたオフセットと同じか大きい)。また、バッファの圧縮、透過的なバッファサイズの管理などの豊富なセマンティクスも提供する必要があります。
Javaでは、(少なくとも)ノンブロッキングネットワークio(NIO)パッケージは、一般的なデータ構造を提供するためのデータ構造(ByteBufferなど)のセットも提供します。
Cにはそのようなデータ構造が存在するか、独自にロールする必要があります。取得したら、使用可能な限り多くのデータを読み取り、オーバーフローなどの問題をバッファーで管理する(たとえば、メッセージフレームの境界を越えてバイトを読み取る)ようにし、マーカーオフセットを使用して、処理したバイトをマークオフします。
Androidが指摘したように、開いている接続ごとに一致するバッファーを作成する必要があります(ほとんどの場合)。
開いているファイル記述子ごとにバッファを持つ構造体を作成し、recv()が0を返すか、バッファで処理するのに十分なデータが得られるまで、これらのバッファを蓄積します。
あなたの質問を正しく理解している場合、非ブロッキングでは複数の接続で同じバッファに書き込む(グローバルの場合)か、または小さなデータを書き込む(ローカルの場合)ため、バッファリングできません。
いずれの場合でも、プログラムはデータの送信元(おそらくファイル記述子による)を識別し、それに応じてバッファリングする必要があります。
スレッディングもオプションであり、多くの人がそうだと思うほど怖くはありません。
Ryan Dahlのevcomライブラリ。これはまさに望みどおりの動作をします。
私は仕事でそれを使用し、うまく機能しています。ただし、非同期DNS解決機能はない(ただし、近日公開予定)ことに注意してください。ライアンは、そのために Michael Tokarevによるudns を提案しています。現在getaddrinfo()をブロックする代わりにudnsを採用しようとしています。