1

最初の9バイトを読み取ることになっています。これには、データパケットのプロトコルと着信サイズが含まれている必要があります。

完了ポートが9バイトで戻ってきたら、どちらを実行するのがよいでしょうか。(パフォーマンス/グッドプラクティスまたは美学の観点から)

  1. ソケットに別の重複した読み取りを投稿します。今回はパケットのサイズを使用して、次の完了で受信できるようにしますか?
  2. ブロッキングソケットを使用してルーチン内でパケット全体を読み取り、9バイトのrecvとオーバーラップした別のパケットを投稿しますか?
  3. チャンクで読み取る(サイズを決定する)たとえば--4096であり、データが読み取られるまで(すべてのパケットが読み取られるまで12回完了すると言う)、重複する各完了を読み取り続けるためのカウンターがあります。
4

2 に答える 2

1

Vladの答えは興味深いですが、OSにとらわれず理論的です。ここでは、IOCP の設計上の考慮事項にもう少し焦点を当てています。

TCP 接続からメッセージのストリームを読み取っているようです。これにより、メッセージは完全なメッセージの長さを詳述するヘッダーで構成されます。ヘッダーは固定サイズで、9 バイトです。

オーバーラップした各読み取り完了は、1 バイトとバッファーのサイズの間で返されることに注意してください。9 バイトの読み取りを発行して、常に完全なヘッダーを取得できると想定してはいけません。完全なメッセージに十分な大きさのバッファーを使用して、読み取りが完了したときにそのメッセージ全体を受信します。予想よりも少ないバイトを返す完了に対処する必要があります。これに対処する最善の方法は、WSABUF ポインターをバッファーの先頭に調整して、後続のオーバーラップ読み取りがその位置のバッファーにより多くのデータを読み取るようにすることです。この読み物が終わった直後に...

データを読み取る最良の方法は、次のことによって異なります。

  • どれくらい大きいか (平均および可能な最大のメッセージサイズ)
  • 処理する可能性のある接続の数
  • メッセージを分割して処理できるか、完全なメッセージとしてのみ処理できるか。
  • ピアがあなたからの応答なしに複数のメッセージを送信できるかどうか、またはそれが「メッセージ応答」スタイルのプロトコルであるかどうか。

IOCP を使用してデータを読み取る方法に関する決定のほとんどは、データのコピーが行われる場所と、処理しているデータをどの程度便利にしたいかによって決まります。ソケット レベルの読み取りバッファリングをオフにしていないと仮定すると、データを読み取るたびにデータ コピーが発生する可能性があります。TCP スタックはソケットごとの読み取りバッファーにデータを蓄積し、重複した読み取りはこれを独自のバッファーにコピーして返します。

最も簡単な状況は、メッセージが到着したときにメッセージを分割して処理できる場合です。この場合、単純にフル バッファ サイズのオーバーラップ読み取りを発行し、完了を処理し (バッファには 1 バイトからバッファ サイズのデータ​​が含まれます)、新しい読み取りを (おそらく同じバッファの最後に) 発行します。処理するのに十分なデータを取得し、さらに読み取る必要があるまでデータを処理します。これの利点は、(バッファー サイズに対して) 最小数のオーバーラップ読み取りを発行することであり、これにより、ユーザー モードからカーネル モードへの遷移が減少します。

メッセージを完全なメッセージとして処理しなければならない場合、それらの処理方法は、メッセージの大きさとバッファの大きさによって異なります。(バッファーの長さが 9 バイトのみであることを指定することによって) ヘッダーの読み取りを発行し、さらに重複した読み取りを発行して、完全なメッセージを 1 つ以上のバッファーに蓄積することができます (バッファーの開始と長さを調整することによって)。 「接続ごと」のデータ構造内でバッファを連鎖させます。または、ヘッダーに対して「特別な」読み取りを発行せず、1 回の読み取りで複数のメッセージが返される可能性に対処します。

私は、このようなことのほとんどを行う IOCP サーバーの例をいくつか持っています。ここからダウンロードして、付随する記事でそれらについて読むことができます。

于 2013-11-20T11:01:43.673 に答える
1

答えは、使用しているインフラストラクチャによって異なります。一般的に、最善の方法は何もしないことです。これは奇妙に聞こえると思いますので、説明させてください。OS が NIC と通信している場合、通常は少なくとも 1 組の RX/TX リング バッファーがあり、コモディティ ハードウェアの場合は、PCIe バスを介してデバイスと通信している可能性があります。PCIe バスの上には、NIC が CPU を使用せずにホスト メモリとの間で読み書きできるようにする DMA エンジンがあります。つまり、NIC がアクティブな間は、CPU の介入を最小限に抑えて、常に独自にパケットの読み取りと書き込みを行います。もちろん、多くの詳細がありますが、一般的にドライバーレベルで何が起こっているかを考えることができます — アプリケーションが何かを読み書きするかどうかに関係なく、読み取りと書き込みは常に DMA を使用して NIC によって実行されます。か否か。今、その上に、ユーザー空間アプリケーションが NIC との間でデータを送受信できるようにする OS インフラストラクチャがあります。ソケットを開くと、OS はアプリケーションが関心のあるデータの種類を判断し、ネットワーク インターフェイスと通信するアプリケーションのリストにエントリを追加します。これが発生すると、アプリケーションは、カーネル内のある種のアプリケーション キューに配置されたデータの受信を開始します。read を呼び出すかどうかは関係ありません。データはそこに配置されます。データが配置されると、アプリケーションは通知を受けます。カーネル内の通知メカニズムはさまざまですが、それらはすべて同様のアイデアを共有しています — データが呼び出せることをアプリケーションに知らせます OS は、アプリケーションが関心を持っているデータの種類を判断し、ネットワーク インターフェイスと通信するアプリケーションのリストにエントリを追加します。これが発生すると、アプリケーションは、カーネル内のある種のアプリケーション キューに配置されたデータの受信を開始します。read を呼び出すかどうかは関係ありません。データはそこに配置されます。データが配置されると、アプリケーションは通知を受けます。カーネル内の通知メカニズムはさまざまですが、それらはすべて同様のアイデアを共有しています — データが呼び出せることをアプリケーションに知らせます OS は、アプリケーションが関心を持っているデータの種類を判断し、ネットワーク インターフェイスと通信するアプリケーションのリストにエントリを追加します。これが発生すると、アプリケーションは、カーネル内のある種のアプリケーション キューに配置されたデータの受信を開始します。read を呼び出すかどうかは関係ありません。データはそこに配置されます。データが配置されると、アプリケーションは通知を受けます。カーネル内の通知メカニズムはさまざまですが、それらはすべて同様のアイデアを共有しています — データが呼び出せることをアプリケーションに知らせます read を呼び出すかどうかに関係なく、データはそこに配置されます。データが配置されると、アプリケーションは通知を受けます。カーネル内の通知メカニズムはさまざまですが、それらはすべて同様のアイデアを共有しています — データが呼び出せることをアプリケーションに知らせます read を呼び出すかどうかに関係なく、データはそこに配置されます。データが配置されると、アプリケーションは通知を受けます。カーネル内の通知メカニズムはさまざまですが、それらはすべて同様のアイデアを共有しています — データが呼び出せることをアプリケーションに知らせますread(). データがその「キュー」に入ると、アプリケーションは呼び出してそれを取得できますread(). ブロッキング読み取りと非ブロッキング読み取りの違いは単純です。読み取りがブロッキングの場合、カーネルはデータが到着するまでアプリケーションの実行を単に中断します。ノンブロッキング読み取りの場合、データの有無にかかわらず、制御はアプリケーションに返されます。後者が発生した場合、アプリケーションは試行を続ける (つまり、ソケットでスピンする) か、データが利用可能であるというカーネルからの通知を待ってから、読み取りに進むことができます。それでは、「何もしない」に戻りましょう。つまり、ソケットは一度だけ通知を受け取るように登録されています。登録が完了すると、アプリケーションは何もする必要がなく、「データがそこにあります」という通知を受け取ります。したがって、アプリケーションがすべきことは、その通知をリッスンし、データが存在する場合にのみ読み取りを実行することです。十分なデータが受信されると、アプリは何らかの方法で処理を開始できます。以上のことを踏まえて、3 つのアプローチのうちどれが優れているかを見てみましょう...

ソケットに別のオーバーラップ読み取りを投稿します。今回はパケットのサイズで、次の完了でそれを受け取りますか?

これは良いアプローチです。理想的には、何も「投稿」する必要はありませんが、これは OS インターフェースがどれだけ優れているかによって異なります。アプリケーションを一度「登録」できず、新しいデータが利用可能になるたびに通知を受信し続け、利用可能になったときに read() を呼び出す場合は、非同期読み取り要求をポストすることが次善の策です。

ブロックソケットを使用してルーチン内でパケット全体を読み取り、recv と重複する別のパケットを 9 バイトで送信しますか?

これは、アプリケーションが他に何もすることがなく、読み取るソケットが 1 つしかない場合に適した方法です。言い換えれば、これは簡単な方法であり、プログラムも非常に簡単で、OS 自体が補完を処理するなどです。ただし、読み取るソケットが複数ある場合は、ソケットごとにスレッドを持つような非常にばかげたこと (ひどい!)、または最初のアプローチを使用してアプリケーションを書き直します。

チャンクで読み取り(サイズを決定)、たとえば-4096で、データが読み取られるまで重複した各完了を読み取り続けるカウンターがあります(すべてのパケットが読み取られるまで12回完了するとします)。

これが行く方法です!実際、これはアプローチ 1 とほぼ同じですが、カーネルへのラウンドトリップを可能な限り減らし、一度に可能な限り多くを読み取るように最適化されています。最初に、これらの詳細で最初のアプローチを修正したかったのですが、あなたが自分でそれを行ったことに気付きました。

それが役に立てば幸い。幸運を!

于 2013-02-16T14:41:50.200 に答える