0

設計中の iPhone アプリケーション用のサーバー アプリケーションを作成しています。iPhone アプリは C# (MonoTouch) で作成され、サーバーも C# (.NET 4.0) で作成されています。

ネットワーク層に非同期ソケットを使用しています。サーバーにより、2 つ以上の iPhone (「デバイス」) が相互に接続し、データを双方向に送信できるようになります。着信メッセージに応じて、サーバーはメッセージ自体を処理するか、送信デバイスと同じグループ内の他のデバイスにデータを中継します。この決定は、最初にパケットのヘッダーをデコードし、それがどのタイプのパケットであるかを判断することによって行うことができます。

これは、最初の 8 バイトが 2 つの整数、ヘッダーの長さとペイロードの長さ (ヘッダーよりもはるかに長くなる可能性があります) になるようにストリームをフレーミングすることによって行われます。

サーバーは、ソケットから最初の 8 バイトを (非同期で) 読み取るため、2 つのセクションの長さになります。次に、ヘッダー セクションの全長まで再度読み取ります。

次に、ヘッダーを逆シリアル化し、その中の情報に基づいて、残りのデータ (ペイロード) を別のデバイスに転送する必要があるかどうか、またはサーバー自体が処理する必要があるかどうかを確認できます。別のデバイスに転送する必要がある場合、次のステップは、ソケットに入ってくるデータをたとえば 1024 バイトのチャンクで読み取り、受信側デバイスに接続されている別のソケットを介して非同期送信を使用してこれらを直接書き込むことです。

これにより、サーバーのメモリ要件が軽減されます。パケット全体をバッファーにロードしてから、ネットワークを介して受信者に再送信するわけではないからです。

ただし、非同期ソケットの性質上、1 回の読み取りでペイロード全体を受信できるとは限らないため、すべてのバイトを受信するまで読み取りを続ける必要があります。最終宛先に中継する場合、これは、送信者から受信したバイトのチャンクごとに BeginSend() を呼び出し、そのチャンクを一度に 1 チャンクずつ受信者に転送することを意味します。

これに関する問題は、非同期ソケットを使用しているため、別のスレッドが同じ受信者 (したがって同じ最終宛先ソケット) で同様の操作を実行する可能性が残り、両方のスレッドからのチャンクが発生する可能性が高いことです。混乱して、その受信者に送信されるすべてのデータが破損します。例: 最初のスレッドがチャンクを送信し、送信者からの次のチャンクを待機している場合 (それを中継できるようにするため)、2 番目のスレッドはデータのチャンクの 1 つを送信し、最初のスレッド (および2番目のスレッドのそれについては)データ。

これを書いているとき、ソケット オブジェクトをロックするのと同じくらい簡単なのだろうか?! これは正しいオプションでしょうか、それとも他の問題を引き起こす可能性がありますか (例: リモート デバイスから送り返されているロックされたソケットを介してデータを受信する際の問題?)

前もって感謝します!

4

3 に答える 3

2

しばらく前に同様のシナリオに直面していましたが、完全な解決策はもうありませんが、私がやったことのほとんどは次のとおりです。

  • 私は同期ソケットを使用しませんでした。C# で非同期ソケットを調査することにしました - ファンライド
  • 本当に必要でない限り、複数のスレッドが単一のリソースを共有することを許可しません
  • 私の「パケット」には、メッセージのサイズ、インデックス、合計パケット数に関する情報が含まれていました
  • 私のパケットの最初のバイトは、それがメッセージの開始であることを示すために固有のものでした。私は 0xAA を使用しました
  • 私のパケットの最後の 2 バイトは、CRC-CCITT チェックサム (ushort) の結果でした
  • 受信ビットを実行したオブジェクトには、受信したすべてのバイトを含むバッファーが含まれていました。そのバッファから、サイズがOKでチェックサムが一致したら、「完全な」メッセージを抽出していました
  • 私が行う必要があった唯一の「ロック」は、一時バッファー内にあったため、書き込み/読み取り操作の間でその内容を安全に分析できました。

少し役立つことを願っています

于 2011-05-31T14:01:11.937 に答える
0

みんなの助けに感謝します。最も簡単な方法は、クライアントで同期送信コマンドを使用するか、少なくとも次のアイテムが送信される前に完了する必要がある送信コマンドを使用することです。アプリのさまざまな部分が何かを送信する必要があるときに send() を呼び出すのではなく、クライアント上の独自の送信キューでこれを処理します。

于 2011-06-07T06:55:18.203 に答える
0

どこに問題があるのか​​ わかりません。サーバーについて言及したので、TCPを想定していますよね?

電話は、PDU の一部を別の電話に通信する必要があります。クライアントとして他の電話のサーバーに接続します。ソケットペアが確立されます。サーバーソケットにデータを送信します。ソケット ペアは一意です。2 台の電話間で発生している可能性のある他のストリームがこれを中断することはありません (もちろん、遅くなります)。

正しく実装されていると仮定して、非同期/同期ソケットがこれにどのように影響するかはわかりません。どちらも正常に機能するはずです。

ここで見えないものはありますか?

ところで、「AA」開始バイトを追加することでプロトコルを強化するという Maciek の計画は優れたアイデアです。最初の要素として長さだけを送信することに依存するプロトコルは、常に最終的に台無しになり、ノードがより多くのバイトをデキューしようとする結果になります。宇宙には原子があります。

Rgds、マーティン

OK、これで問題がわかりました(OPネットワークのトポロジを完全に誤解していました-各電話はTCPサーバーとクライアント/秒を実行していると思いましたが、PC/チャットルームにはサーバーが1つしかありません)。ミューテックスでソケット クラスをロックできなかった理由がわかりません。メッセージをシリアル化します。メッセージをソケットのキューに入れることもできますが、これには回避しようとしているメモリへの影響があります。

たとえば、「別のソケット接続を開いてこの GUID を返すと、ソケットでメッセージがストリーミングされます」など、電話への指示のみを提供する接続を専用にすることができます。これは、制御のためだけにソケットペアを使い果たし、サーバーの容量を半分にします:(

説明したプロトコルに固執していますか、またはメッセージをチャンクに分割し、各チャンクに ID を付けることはできますか? その後、メッセージを 1 つのソケット ペアに多重化できます。

メッセージをチャンク化する必要がある別の代替手段は、電話がメッセージ ID を確立するために使用するメッセージ ID (GUID?) を含む「コントロール メッセージ」(おそらく AA ではなく開始時に 55 のチャンク) を導入することです。サーバーへの 2 番目のソケット接続は ID を渡し、新しいソケット接続で 2 番目のメッセージが送信されます。

新しいメッセージが待っている可能性があることを電話に認識させるもう 1 つの方法 (もう飽きた?) は、電話がメッセージを受信して​​いるサーバー ソケットを閉じることです。その後、電話は再び接続し、メッセージ ID yyyy の xxxx バイトのみを取得したことをサーバーに伝えることができます。サーバーは、新しいメッセージ zzzz 用に別のソケットを開く命令で応答し、メッセージ yyyy の送信を再開できます。これには、「中断」中にデータが失われないようにするために、サーバーでのバッファリングが必要になる場合があります。とにかく、この種の「中断後にストリーミングを再開する」機能を実装することをお勧めします。これは、360MB のビデオ ファイルの最後の KB がストリーミングされているときに電話がブリッジ/トンネルを通過する傾向があるためです:( TCP がドロップされたパケットを処理する必要があることはわかっています。 、

これらのソリューションのどれも、特に満足のいくものではありません。他のアイデアがどのように生まれるのか興味があります..

Rgds、マーティン

于 2011-05-31T14:37:26.930 に答える