2

Linuxでp2pアプリケーションを作成しようとしていますが、これをできるだけ効率的に実行したいと考えています。

私が抱えている問題は、パケットの管理にあります。ご存知のように、recv()バッファには常に複数のパケットが存在する可能性があるため、複数のパケットが1つの大きなパケットとして扱われないようにするために何らかのメッセージフレーミングシステムが必要です。

したがって、現時点での私のパケット構造は次のとおりです。

(u16int Packet Length):(Packet Data)

これには、recv()を2回呼び出す必要があります。1つはパケットサイズを取得し、もう1つはパケットを取得します。

これには2つの主な問題があります。

1. A malicious peer could send a packet with a size header of 
  something large, but not send any more data. The application will 
  hang on the second recv(), waiting for data that will never come.
2. Assuming that calling Recv() has a noticeable performance penalty
  (I actually have no idea, correct me if I am wrong) calling Recv() twice 
  will slow the program down.

最高の効率と安定性の両方のためにパケット/受信システムを構築するための最良の方法は何ですか?他のアプリケーションはどのようにそれを行いますか?おすすめは何ですか?

前もって感謝します。

4

5 に答える 5

5

TCPストリーム内のメッセージの「フレーミング」は正しいと思います。

フレームを読んでいることが明らかになるように、各フレームの前に「マジッククッキー」を配置することを検討できます(たとえば、パケット長に加えて、各フレームヘッダーの上部に32ビットint「0xdeadbeef」を書き込みます)。各recv()ペアの最初のヘッダー。メッセージの先頭に魔法の整数が存在しない場合は、同期がとれていないため、接続を切断する必要があります。

複数のrecv()呼び出しは、パフォーマンスに影響を与える可能性はありません。実際のところ、TCPメッセージは予測できない方法でセグメント化、合体、およびストールする可能性があるため、期待するすべてのデータを取得するまで、ループでrecv()を呼び出す必要があります。これには、2バイトのヘッダーと、ペイロードバイトのより大きな読み取りが含まれます。2バイトのバッファを使用して「recv」を呼び出してメッセージの「サイズ」を読み取ることは完全に可能ですが、返されるのは1バイトだけです。(もう一度recvを呼び出すと、後続のバイトが取得されます)。私がチームの開発者に伝えていること-recvが一度に1バイトしか配信できない可能性があるかのように、ネットワークパーサーをコーディングします。

非ブロッキングソケットと「select」呼び出しを使用して、ハングを回避できます。データが妥当な時間内に到着しない場合(または予想よりも多くのデータが到着する場合、次のメッセージでの同期が不可能になるなど)、接続を切断するだけです。

私は自分のP2Pプロジェクトに取り組んでいます。ノートを交換したいと思います。よろしければ、オフラインでフォローアップしてください。

于 2011-11-16T07:57:11.787 に答える
3

他の人には同意しません。TCPは信頼できるプロトコルなので、クライアントコードが安定していないか、一方的なクライアントがポート番号に接続することを恐れない限り、パケットマジックヘッダーは役に立ちません。

クライアントごとにバッファを作成し、非ブロッキングソケットと///を使用selectします。クライアントから利用可能なデータがある場合は、できるだけ多くのデータを読み取ってください。さらに「パケット」を読み取ってもかまいません。次に、サイズフィールドが使用できるように十分に読んだかどうかを確認します。読んだ場合は、パケット全体(またはそれ以上)を読んだことを確認します。その場合は、パケットを処理します。その後、さらにデータがある場合は、この手順を繰り返すことができます。部分的なパケットが残っている場合は、それをバッファの先頭に移動するか、循環バッファを使用して、これらのメモリを実行する必要がないようにすることができます。pollepollkqueue

クライアントのタイムアウトは、select/...ループで処理できます。

これは、受信したパケットデータで複雑なことをしている場合に使用するものです。結果をファイルに(より大きなチャンクで)書き込むだけの場合、sendfile/spliceはより良いパフォーマンスをもたらします。パケットの長さを読み取るだけで(複数の読み取りの場合もあります)、パケット全体を読み取るまで、複数の呼び出しを使用してsendfileを送信します(残りの読み取り量を追跡します)。

于 2011-11-20T12:19:55.307 に答える
1

(ソケットにSOCK_NONBLOCKを設定することにより)への非ブロッキング呼び出しを使用し、ループで(タイムアウトrecv()を使用して)データを読み取る準備ができるまで待機できます。select()

次に、ファイル記述子が「データを待機中」の状態になっている時間が長すぎる場合は、ソケットを閉じることができます。

于 2011-11-16T07:50:14.607 に答える
1

TCPはストリーム指向のプロトコルであり、実際にはパケットの概念はありません。したがって、1回の呼び出しで複数のアプリケーション層パケットを受信することに加えて、アプリケーション層パケットの一部recv()のみを受信し、残りは将来の呼び出しで受信する場合もあります。recv()

recv()これは、各呼び出しで可能な限り多くのデータを受信し、少なくとも1つの完全なアプリケーション層パケットが得られるまでそのデータをアプリケーション層バッファーにバッファリングすることによって、堅牢な受信者の動作が得られることを意味します。これにより、2回の呼び出しでrecv()問題が発生することも回避されます。

ブロックせずに、それぞれで可能な限り多くのデータを常に受信するにrecv()は、非ブロックソケットを使用し、に設定されたrecv()-1が返されるまで呼び出す必要があります。errnoEWOULDBLOCK

于 2011-11-20T11:49:18.730 に答える
0

他の人が言ったように、先頭のマジックナンバー(OT:man file)はデータグラム境界を識別するための良い(99.999999%)ソリューションであり、タイムアウト(ノンブロッキングrecv()を使用)は欠落/遅延パケットを検出するのに適しています。

攻撃者を頼りにする場合は、パケットにCRCを入れる必要があります。プロの攻撃者が本当に望んでいる場合、彼/彼女は遅かれ早かれあなたのCRCがどのように機能するかを理解しますが、CRCなしでパケットを作成するよりもさらに困難です。(また、安全性が重要な場合は、ネット上にSSL libs / examples / codeがあります。)

于 2011-11-16T08:58:02.837 に答える