2

これは基本的に、複数の PC にインストールされる 1 つのアプリであり、インストールごとに独自のデータベースが維持されます。このデータベースは、同時に稼働している (同じネットワークに接続されている) ときに他のデータベースと同期されます。

私は単純なソケット接続とカスタム バッファを使用してこれをテストしましたが、アプリ間の通信を受け入れられた標準に準拠させ、安全で堅牢にすることを望んでおり、車輪を再発明しようとはしません。

このアプリ間の通信を行う通常/標準的な方法は何ですか? 詳細はどこで確認できますか?

また、ネットワーク上の他のアプリをアナウンスしたり見つけたりするために、どのようなテクニックを使用できますか?


編集:(私の問題を改善する)

以下の gimel が指摘する pub/sub モデルは、私が必要としているものに沿っているようです。しかし、それは多くの分野をカバーしており、それらすべてから何を取り除いて使用するべきか本当にわかりません。

また、2 つ以上のアプリがお互いを見つけたら、P2P 接続を確立する必要があるようです。どうすればよいですか?

利用可能な例/チュートリアルがある場合は、それらを指摘してください。私が必要とするようなものを実装する小さなオープンソースプロジェクト/モジュールも役立ちます。

私が選んだプラットフォームは Linux ですが、Windows ベースの例も非常に使いやすいでしょう。


編集 [09-01-06]:

現在、次のオプションを検討しています。

  1. マルチキャスト(TLDP-Howto) - これは機能するようですが、もう少し勉強する必要があります。
  2. 無料の動的 DNS サーバーを使用しますが、これは少し危険に思えます...
  3. gmail/yahoo/...などの無料の電子メール機能を使用し、そこからメールを送信/読んで、他のアプリのIPを見つけます(機能しますが、汚いと感じます)
  4. webservices が提案されましたが、それらがどのように機能するのかわかりません。詳しく調べる必要があります

これらのオプションについてご意見をお聞かせください。また、例があれば教えてください。残念ながら、中央サーバーまたはウェブサイトを使用するオプションはありません (無料で永続的であることが保証されている場合を除きます)。

[2009 年 2 月 19 日編集]

(2つまたは3つの回答を受け入れることができたらいいのにと思います!私が受け入れたのは、考え方と可能性を提供するためであり、他の人は固定されているが適用可能な解決策を提供していました。回答してくれたすべての人に感謝します。すべてが役に立ちます。)

解決策を見つけて実装したら、この質問を更新します。解決策が適切であれば、そのための sourceforge プロジェクトを作成します。(いずれにせよ、はるかに大きなプロジェクト内の小さな問題です。)

4

12 に答える 12

5

パブリッシュ/サブスクライブの非同期メッセージング パラダイムを参照してください。

実装例はApache ActiveMQです。

Apache ActiveMQ は高速で、多くの言語間クライアントとプロトコルをサポートし、使いやすいエンタープライズ統合パターンと多くの高度な機能を備え、JMS 1.1 と J2EE 1.4 を完全にサポートします。

于 2008-12-24T07:25:46.067 に答える
4

うーん、

これは数学の問題に少し似ています。2 台のコンピューターがお互いを見つけた後、どのように接続を確立するかという問題は、かなり単純明快です。任意の数の P2P またはクライアント サーバー プロトコルを使用できます。SSLはほぼどこでも利用できますが、 SSHを提供したり、 Freenetを実行したり、何でもできます。これらのプロトコルのいずれかを介して接続を確立すると、データ交換用のパブリッシュ/サブスクライブ モデルがうまく機能する可能性があります。しかし〜がある

コンピュータがどのようにお互いを見つけるかという問題は、物事が複雑になるところです。基本的に次の 3 つのケースがあります。

  1. すべてのコンピュータは、独自のローカル エリア ネットワーク上にあります。 この場合、オンラインになったコンピュータは、自分がオンラインであるというメッセージをブロードキャストし、他のどのマシンがオンラインであるかに関するメッセージを返すことができます。ブロードキャスト/マルチキャスト メッセージは、ネットワーク上のすべてのマシンに送信する必要があることに注意してください。これは、ターゲットが不明なためです。ネット上のすべてのマシンにメッセージを送信できないため、これをインターネット上で行うことはできません。

  2. あなたのコンピューターはインターネット上の任意のノードにあります **そして** あなたのコンピューターの少なくとも 1 台は常にウェブに接続されています **そして** すべてのマシンは定期的にオンラインになります。 この場合、各マシンは実行中の IP アドレスのリストを保持できます。しばらくオフラインだったマシンがオンラインに戻ると、既知のアドレスをチェックし、最初の有効なアドレスに接続します。これが、エミュール プロトコルがサーバーを見つける方法です。

  3. あなたのコンピュータは、インターネット上の任意のノードにあり **すべてのマシンが一緒にオフラインになっています**または**他のコンピュータが IP アドレスを切り替えている間、かなりの数のコンピュータが長期間オフラインになります。ここで、登場する新しいマシンがお互いを見つける方法がない ことを証明できるとは思いません。いくつかの中央サーバーを共通の IP にリンクします。メッセージは大きいため、インターネット全体にメッセージをブロードキャストする方法はありません。このような状況では、コンピューターにはオンラインになる他のマシンの識別情報がないため、中央の共通リソースを使用する必要があります: あなたが言及した電子メールアドレス、Web サイト、ftp サーバー、IRC チャネルなどです。ダイナミック DNS は、一元化されたインフォメーション ストアのもう 1 つのインスタンスにすぎません。そのようなストアを使用する必要がある場合は、当然、使用可能なすべてのストアの信頼性、速度、永続性を評価する必要があります。この点に関して、あなたのアプリケーションが何を必要としているのかについて、あなたがより多くの情報を提供しない限り、あなたが必要とする永続ストアを他の誰かが決めることはできないと思います。

これはすべての可能性をカバーしていると思います。あなたがしなければならないことは、アプリケーションがこれらの一般的なケースのどれに該当するかを決定し、次にこのケースに適合するプロトコルの 1 つを決定することです。

編集:

フィードバックから、ケース 3 が当てはまるようです。上記の理由から、何らかの形式の外部サーバーを回避する方法がないことは明らかです。干し草の山から針を見つける唯一の方法は、その場所を追跡することです。このようなプロバイダーに求められる資質は次のとおりです。

  • 信頼性: 1 日にどれくらいの頻度で稼働するか
  • 速度: どのくらいの速さで反応するか
  • 永続性: その物はどのくらい続くと思いますか? インターネットが進化するにつれて、アクセスできなくなりますか?

前述のように、この法案に多かれ少なかれ適合する、簡単に利用できるリソースが多数あります。電子メール サーバー (現在使用している)、Web サーバー、FTP サーバー、DNS サーバー、IRC チャネル、Twitter アカウント、Web フォーラム....

しばらくするとアプリケーションが起動し、中央サーバーなしで更新が必要になるという問題はよくある問題ですが、主にウイルス作成者の間でよく見られます。分散アプリケーションを作成するためのリソースを持っている組織には、中央サーバーを維持するためのリソースもあります。サーバ。とはいえ、長年にわたる標準的なソリューションには、電子メール、http サーバー、ftp サーバー、IRC チャネル、動的 DNS サーバーが含まれていました。これらの各カテゴリの異なるサーバーは、速度、信頼性、および永続性が異なるため、いずれかを選択するタスクはあなたの判断に委ねられます. IRC チャネルは、セットアップが速くて簡単なので言及する価値がありますが、インターネットが進化するにつれて、これらは実際に衰退する可能性があります.

さまざまな「クライアント検索」手法を使用する配布アプリケーションの例として、ソースを BO2Kにダウンロードできます。これは、「リモート管理ユーティリティ」です。これにより、リモート更新クライアントのすべての機能についてある程度の洞察が得られる場合があります。

繰り返すだけです。あなたの問題には3つの部分があると思います:

  1. お互いを見つけるマシン (上記参照)
  2. 接続を確立するマシン (ここでも、SSL、SSH などはすぐに利用できます)
  3. データを交換するマシン。「パブリッシュ/サブスクライブ」モデルを使用するか、独自の単純なプロトコルをロールするだけです。私は自動更新クライアントを持っている会社で働いていました。独自のプロトコルを作成する理由は、1) 最も単純な状況であっても、交換されるデータに応じて速度と信頼性の要件が異なるためです。非常に単純なデータ交換のためのプロトコル 3. さまざまなアプリケーションがさまざまな方法と言語を使用するため、単純なデータ交換のためのプロトコルは支配的ではありません。より複雑な状況では、実際にはプロトコルの森全体がありますが、それらの複雑さはさまざまであり、単純なデータ交換に使用するのは厄介です. git scmの方法send data は更新プロトコルの一例です。たまたまデータベースが git が送信するソース コードに似ている場合は、git を使用してデータベースを維持できます。しかし、更新のアプローチは、git が行うものとは似ていない可能性があります。もう 1 つのプロトコルの例は、SOAP などのWeb サービスの 1 つまたは別のバージョンです。これらのプロトコルはラップするだけですxml と http を使用して 1 台のマシンで関数を呼び出すプロセス。アプリケーション間のソケット通信をすでに確立できる場合は、これを行う理由はありません。Web サービスを実装するには、http サーバーを実行し、http クライアントの xml を生データに解析する必要があることを思い出してください。ソケットを介してデータを直接送信できることを考えると、これを行う理由はありません。したがって、自分でロールバックすることに戻ります。

とにかく、単純なプロトコルの例は次のようになります。

  • あるアプリは、インデックスの配列として持っているデータのインデックスを最初に送信します。
  • 他のアプリは新しいアイテムのリストを送信します
  • 次に、最初のアプリがそれらの実際のアイテムを送信します。

その後、アプリは役割を変更し、逆方向にデータを交換します。プロトコルの特定の「ハンドシェイク」は、擬似コードで次のようになります。

void update_database(in_stream, out_stream) {
  Get_Index_Of_Other_Machines_Items(in_stream);
  Send_Index_Of_Items_You_Need(out_stream);
  Get_Items_You_Need(in_stream);
}

これらのデータ交換の反対側を実装する別の関数を使用します。

さらに考慮すべきことは、データベース内の各エントリが個別に生成されている場合、分散データベース内のすべての項目を参照できなくても、一意の ID を生成する必要があるということです。この目的のために、 GUIDまたは単に大きな乱数を生成できます。

これを使用する場合は、間違いなくこれをさらに調整して開発する必要があります。

ただし、すべてのアプリケーションがたまにしか更新されない場合は、特定のインスタンスにデータ項目があることを確認する方法がないことに注意してください。たとえば、1 日のうち、半数のマシンが午後 5 時以降にオンラインになり、残りの半数のマシンが午後 5 時までにオンラインになるとします。この場合、マシンの 2 つのグループは、互いに直接更新する場合、データをまったく共有しません。一方、あなたのマシンが (私が説明したようなパターンに従ってではなく) 均等に分散された時間で実際に動作する場合、各マシンが最終的にすべての更新 (少なくともすべての古い更新) を取得する可能性がかなり高くなります。 .

これらすべてを考慮して、アプリケーションが接続する頻度がどれくらい低くなるか、すべてのデータが完全に同期されることがどれほど重要かを正確に検討する必要があると思います. 非常にまれな状況では、外部サーバーを使用してデータを移動したり、クライアントを見つけたりする必要がある場合があります。通常の電子メールは、この問題に対する自然な解決策です。別のコンピュータがオンラインであるときにオンラインになっているコンピュータがない場合に使用するのが 1 つの方法です。特定の電子メール アカウントへのアクセスが心配な場合は、複数の電子メール アドレスのリストを使用して各アプリを起動し、すべてのアドレスをチェックして、特定のアドレスが失敗したときにさらに多くのアドレスでアプリケーションを更新するオプションを使用できます。すべてに電子メールを使用することには、単純さの利点があります。あなたが使用することができます

軽い言い方をすれば、ナンバー ステーションは、情報更新の問題を解決するためのインターネット以前の取り組みですが、あなたの場合には当てはまりません (ただし、ナンバー ステーションは世界の大部分に放送されます)。

于 2009-02-16T22:57:11.150 に答える
3

数年前に、あなたが説明しているものと同様のアプリを設計しました。各デスクトップで実行される「アドバタイズ サーバー」を設計し、UDP を使用してそのステータスをネットワーク上で実行されている他のプログラムにブロードキャストしました。さて、これには、このアプリケーションの実行方法に応じて、独自の問題があります...しかし、これがどのように機能したかを簡単に説明します...

データベースサーバーとアプリケーションが接続されているデータベースをハッシュすることによって選択されたポートに「リスナー」をセットアップしました。これにより、誰でもブロードキャストを受信し、私と同じデータベースを使用し、アプリの複数のインスタンスをデスクトップで実行できるようになります (設計要件)。

次に、特定のイベントをブロードキャストするさまざまな「BroadcastMessage()」関数をセットアップします。API を使用する開発者が、カスタム ペイロード データを使用してカスタム イベントを作成し、プログラムにそのイベントのリスナーを登録させ、そのイベントが発生したときにレジストラーに通知し、それを渡すことができるようにしました。付いてきたデータ。

たとえば、アプリが起動すると、「私はここにいます」というメッセージをブロードキャストし、聞いている人は誰でもメッセージを食べたり、無視したり、返信したりできます。「I'm here」には、実行中のアプリケーションの IP アドレスが含まれていたため、クライアントは TCP 接続を介して接続し、さらにデータを更新して配信することができました。

UDP を選択したのは、実行中の他のすべてのインスタンスがこれらのブロードキャストを表示する必要がないためです。それは何よりも便利でした...同じ画面にいる間に誰かがDBにレコードを追加した場合、新しいレコードはデスクトップに「表示」されます。

また、アプリの実行中に管理者がユーザーの権限を変更した場合、ユーザーはアプリを終了して再入力する必要がなく、更新が受信され、その場で処理され、ユーザーはそれを行うことができるので便利でした。彼らがする必要があること。

これらのタイプのメッセージをリッスンするだけのスレッドでリスナーをセットアップするのは本当に簡単です...サンプルコードが必要な場合は、それも提供できますが、それはC++であり、Windows用に設計されていますが、生のwsock32を使用しています.lib であるため、任意の Unix プラットフォームに非常に簡単に移行する必要があります。(私はそれをたくさん使ったので、DWORDをtypedefする必要があります..)。

于 2009-02-12T20:08:18.763 に答える
2

これを完全に P2P にしたいですか、それともディレクトリ以外のことをする中央サーバーを用意する予定ですか?

通信のセキュリティについては、SSL で問題ありません。Java を使用している場合、Java はこれらを非常に簡単な方法でサポートします。Java 6 の SSLのリファレンスは次のとおりです。

于 2008-12-24T07:45:00.223 に答える
1

Bittorrentタイプのセットアップを使用することを検討しましたか?

使用される通信プリンシパルは、アプリケーションを構築するためのかなり強固な基盤を提供する必要があります。必要なのは、2つのノードがお互いを認識し、そこから構築することだけです。私はMonoTorrentを使用してプライベート(100ノード)データネットワークを実行し、RSSフィードを使用して、どのファイルをどこに配置する必要があるか(Wordpressの変更バージョン)を通知し、SSHトンネルですべてを実行します。私はネットワークを管理する中央サーバーを持っていますが、それは私の100ノードのいずれかに簡単に住むことができます。ダイナミックDNSサービスを使用して、生きている最初のノードは、サーバーがダウンした場合のバックアップとして独自のトラッカーを設定します。

XMLファイルをメッセージングスキームとして使用するか、Bittorrentネットワークの送信を変更してデータパケットをアプリに直接送信することができます。あなたが探しているもののコンセプトはBittorrentにあると思います。ネットワーク上にアクティブなホストがなかった場合、最初に起動するノードはダイナミックDNSエントリを再利用します(DynDNSにはかなり使いやすいAPIがあります)。(欠点があります... TTLウィンドウ内で2つのトラッカーが起動すると、同期の問題が発生します)

SSHトンネリングへの参照はかなりありますが、図が楽しいので、これを使用します。SSHトンネリングは、利用可能な最も効率的な方法ではありませんが、プログラムで通信をSSLトンネルでラップする必要がある場合の非常に優れた代替手段です。

私は考えが少し混乱していることを知っています、私はそれがあなた自身を正しい方向に向けるのに少し助けになることを願っています。PS ...完全にポータブルなソリューションの場合、これをJavaまたは.Netで実行できます(Monoで実行しています。AppleTVでもMonoを実行しています)。そうすれば、OSは操作の柔軟な部分にさえなる可能性があります。

于 2009-02-18T18:47:31.717 に答える
1

わかりました。MQ とそのタイプのことはやり過ぎのように聞こえます。

あなたのアプリについての私の理解:

同じネットワーク上の複数のマシンで実行されているデスクトップ アプリ - 独自のデータベースがあり、お互いを検出する必要があります。

なぜだめですか:

1)UDPブロードキャスト/「同じネットワーク上の他のマシンを見つける」ために定期的にリッスンします-Javaの例:http://java.sun.com/docs/books/tutorial/networking/datagrams/index.html

2) 検出後の実際の通信には SSL ソケットを使用します:
http://stilius.net/java/java_ssl.php ....
http://www.exampledepot.com/egs/javax.net.ssl/Client.html

于 2009-02-18T00:37:32.650 に答える
1

Ok。お約束どおり、私のアプリケーションから抜粋したサンプル コードの一部を以下に示します。これはコンパイルして実行することは想定されていません。これはが行った方法の例です。あなたは完全に異なることをしなければならないかもしれません。さらに、これは Windows 用に作成されたものであり、コードでわかるように、Windows メッセージを使用してサーバー スレッドとメイン アプリケーションの間でデータを送信しますが、それはすべて、使用方法によって異なります。より興味深い部分をいくつか残しておきましたので、参考にしてください。

セキュリティの部分に関しては、まあ、その部分を処理できると思います。これは、よく知られている暗号を使用して、ネットワークを通過する前にデータを暗号化するという単純な問題であるため、それを含める必要はないと思いました。たとえば、私がどのようにパケット ヘッダーを構築したかを見ることができ、通常は別の構造で構成されるペイロードがあります。したがって、その構造を暗号化してデータとして送信し、反対側で復号化して、適切な構造にコピーします。

// Some defines that you may see in the code, all of which are user defined...
#define ADVERTISE_SERVER           0x12345678 // Some unique ID for your advertisement server
#define ACTIVITY_NONE              0x00000000
#define ACTIVITY_LOGON             0x00000001
#define ACTIVITY_LOGOFF            0x00000002
#define ACTIVITY_RUNNING           0x00000004
#define ACTIVITY_IDLE              0x00000005
#define ACTIVITY_SPECIFIC          0x00000006


enum Advertisements {
   ADVERTISE_SHUTDOWN,
   ADVERTISE_MESSAGE,
   ADVERTISE_DEBUG,
   ADVERTISE_OVERLAPPED,
   ADVERTISE_BROADCAST_IDENTITY,
   ADVERTISE_IDENTITY,
   ADVERTISE_PARAMETER_CHANGE
};

struct TAdvertiseServerPacket {
   UINT     uiAdvertisePacketType;
   DWORD    dwPacketLength;
   bool     bRequestReply;
   UINT     uiReplyType;
   bool     bOverlappedResult;
   int      iPacketId;
   bool     bBroadcast;
   char     GuidHash[35];
   BYTE     PacketData[1024];
};

struct TAdvertiseIdentity {
   TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
   char  szUserName[LEN_APPL_USERNAME + 1];
   char  szDatabase[MAX_PATH];
   char  szConfiguration[MAX_PATH];
   char  szVersion[16];
   long  nUserId;
   char  szApplication[MAX_PATH];
   char  szActivity[33];
   UINT  uiStartupIndc;
};

struct TAdvertiseMessage {
   char              MessageFrom[LEN_APPL_USERNAME + 1];
   char              MessageText[512];
};

struct TAdvertiseItemUpdate {
   NMHDR             pNMHDR;
   long              nItemId;
   long              nItemTypeId;
   char              szItemName[LEN_ITEM_NAME + 1];
   bool              bState;
};

struct TAdvertiseItemUpdateEx {
   NMHDR             pNMHDR;
   long              nItemId;
   bool              bState;
   bool              bBroadcast;
   DWORD             dwDataSize;
   void              *lpBuffer;
};

struct TOverlappedAdvertisement {
   int               iPacketId;
   BYTE              Data[1020];
};

DWORD WINAPI CAdvertiseServer::Go(void* tptr)
{
   CAdvertiseServer *pThis = (CAdvertiseServer*)tptr;

   /* Used and reused for Overlapped results, */
   DWORD BufferSize       = 0;
   BYTE *OverlappedBuffer = NULL;
   bool bOverlapped       = false;
   int  iOverlappedId     = 0;
   DWORD BufferPosition   = 0;
   DWORD BytesRecieved    = 0;
   TAdvertiseItemUpdateEx *itemex = NULL;
   UINT uiPacketNumber    = 0;

   bool Debug = false;
#ifdef _DEBUG
   Debug = true;
#endif
   {
      DWORD dwDebug = 0;
      dwDebug = GetParameter(ADVERTISE_SERVER_DEBUG); // GetParameter is part of the main program used to store running config values.
      if(dwDebug > 0)
      {
         Debug = true;
      }
   }
   WSAData wsaData;
   WSAStartup(MAKEWORD(1,1), &wsaData);
   ServerSocket = socket(PF_INET, SOCK_DGRAM, 0);
   if(ServerSocket == INVALID_SOCKET)
   {
      CLogging Log("Client.log");
      ServerSocket = NULL;
      Log.Log("Could not create server advertisement socket: %d", GetLastError());
      return -1;
   }
   sockaddr_in sin;
   ZeroMemory(&sin, sizeof(sin));
   sin.sin_family = AF_INET;
   sin.sin_port = htons(Port);
   sin.sin_addr.s_addr = INADDR_ANY;
   if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
   {
      CLogging Log("Client.log");
      Log.Log("Could not bind server advertisement socket on port: %d Error: %d", Port, GetLastError());
      DWORD dwPort = 0;
      dwPort = GetParameter(ADVERTISE_SERVER_PORT); // Again, used to set the port number, if one could not be figured out.
      if(dwPort > 0)
      {
         return -1;
      }
      Port = 36221;
      sin.sin_port = htons(Port);
      if(bind(ServerSocket, (sockaddr *)&sin, sizeof(sin)) != 0)
      {
         CLogging Log("Client.log");
         Log.Log("Could not bind server advertisement socket on port: %d Error: %d Could not start AdvertiseServer after two attempts.  Server failed.", Port, GetLastError());
         return -1;
      }
   }

   SECURITY_ATTRIBUTES sa;
   sa.bInheritHandle = TRUE;
   sa.lpSecurityDescriptor = NULL;
   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   HANDLE mutex = CreateMutex(NULL, FALSE, "Client.Mutex"); // Used to keep and eye on the main program, if it shuts down, or dies, we need to die.
   while (1)
   {
      TAdvertiseServerPacket ap;
      sockaddr_in sin;
      int fromlen = sizeof(sin);
      fd_set fds;
      FD_ZERO(&fds);
      FD_SET(ServerSocket, &fds);
      timeval tv;
      tv.tv_sec = 15;
      tv.tv_usec = 0;
      int err = select(0, &fds, NULL, NULL, &tv);
      if(err == SOCKET_ERROR)
      {
         CLogging Log("Client.log");
         Log.Log("Advertise: Winsock error: %d", WSAGetLastError());
         Beep(800, 100);
         break;
      }
      if(err == 0)
      {
         if(WaitForSingleObject(mutex, 0) != WAIT_OBJECT_0)
         {
            continue; // Main app is still running
         }
         else
         {
            Beep(800, 100); // Main app has died, so exit our listen thread.
            break;
         }
      }

      int r = recvfrom(ServerSocket, (char *)&ap, sizeof(ap), 0, (sockaddr *)&sin, &fromlen);

      if(r != sizeof(TAdvertiseServerPacket))
      {
         continue;
      }
      switch(ap.uiAdvertisePacketType)
      {
         // This is where you respond to all your various broadcasts, etc.
         case ADVERTISE_BROADCAST_IDENTITY:
         {
            // None of this code is important, however you do it, is up to you.
            CDataAccess db(CDataAccess::DA_NONE);
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            if(pThis->szActivity) {
               CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_SPECIFIC, pThis->szActivity, false);
            } else {
               CAdvertiseServer::AdvertiseIdentity(ComputerName, CDataAccess::GetLoggedInUserName(), CDataAccess::DatabaseConfiguration(), CDataAccess::DatabaseConfiguration(), ACTIVITY_RUNNING, NULL, false);
            }
         }
         case ADVERTISE_IDENTITY:
         {
            TAdvertiseIdentity ident;
            memcpy((void*)&ident, (void*)ap.PacketData, ap.dwPacketLength);
            Listener::iterator theIterator;
            theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
            if(theIterator == pThis->m_Listeners.end())
            {

               //We got an Identity Broadcast, but we're not listening for them.
               continue;
            }
            {
               itemex = new TAdvertiseItemUpdateEx;
               ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
               memcpy((void*)&ident, ap.PacketData, ap.dwPacketLength);
               itemex->pNMHDR.code     = (*theIterator).first;
               itemex->pNMHDR.hwndFrom = (*theIterator).second;
               itemex->pNMHDR.idFrom   = ADVERTISE_SERVER;
               itemex->dwDataSize      = sizeof(TAdvertiseIdentity);
               itemex->lpBuffer        = (void*)&ident;
               SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
               delete itemex;
            }
         }
         case ADVERTISE_SHUTDOWN:
         {
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            CString guid;
            guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
            if(stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0)
            {
               return 1;
            }
         }
         case ADVERTISE_MESSAGE:
         {
            TAdvertiseMessage msg;
            memcpy((void*)&msg, (void*)ap.PacketData, ap.dwPacketLength);
            CString msgtext;
            msgtext.Format("Message from: %s\r\n\r\n%s", msg.MessageFrom, msg.MessageText);
            ::MessageBox(NULL, msgtext, "Broadcast Message", MB_ICONINFORMATION | MB_SYSTEMMODAL);
            break;
         }
         case ADVERTISE_OVERLAPPED:
         {
            // I left this code in here, as it's a good example of how you can send large amounts of data over a UDP socket, should you need to do it.
            BufferPosition = (1020 * ((ap.uiReplyType - 1) - 1));
            if(BufferPosition > BufferSize) {
               BufferPosition -= 1020;
            }
            TOverlappedAdvertisement item;
            ZeroMemory(&item, sizeof(TOverlappedAdvertisement));
            memcpy((void*)&item, (void*)ap.PacketData, ap.dwPacketLength);
            if(item.iPacketId == iOverlappedId)
            {
               DWORD ToCopy = (sizeof(item.Data) > (BufferSize - BytesRecieved) ? BufferSize - BytesRecieved : sizeof(item.Data));
               memcpy((void*)&OverlappedBuffer[BufferPosition], (void*)item.Data, ToCopy);
               BytesRecieved += ToCopy;
               if(BytesRecieved < BufferSize)
               {
                  continue;
               }
            }
         }
         default:
         {
            // What do we do if we get an advertisement we don't know about?
            Listener::iterator theIterator;
            if(bOverlapped == false)
            {
               theIterator = pThis->m_Listeners.find(ap.uiAdvertisePacketType);
               if(theIterator == pThis->m_Listeners.end())
               {
                  continue;
               }
            }

            // Or it could be a data packet
            TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
            ZeroMemory(ComputerName, sizeof(ComputerName));
            DWORD len = MAX_COMPUTERNAME_LENGTH;
            GetComputerName(ComputerName, &len);
            CString guid;
            guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);
            bool FromUs = stricmp(ap.GuidHash, CDataAccess::HashPassword(guid)) == 0;
            if(((FromUs && Debug) || !FromUs) || ap.bBroadcast)
            {
               if(ap.bOverlappedResult)
               {
                  if(ap.uiReplyType == 1)
                  {
                     itemex = new TAdvertiseItemUpdateEx;
                     ZeroMemory(itemex, sizeof(TAdvertiseItemUpdateEx));
                     memcpy(itemex, ap.PacketData, ap.dwPacketLength);
                     OverlappedBuffer = (BYTE*)malloc(itemex->dwDataSize);
                     BufferSize = itemex->dwDataSize;
                     ZeroMemory(OverlappedBuffer, itemex->dwDataSize);
                     bOverlapped = true;
                     iOverlappedId = ap.iPacketId;
                     uiPacketNumber = ap.uiReplyType;
                  }
                  continue;
               }
               if(bOverlapped)
               {
                  itemex->pNMHDR.code     = (*theIterator).first;
                  itemex->pNMHDR.hwndFrom = (*theIterator).second;
                  itemex->pNMHDR.idFrom   = ADVERTISE_SERVER;
                  itemex->dwDataSize      = BufferSize;
                  itemex->lpBuffer        = (void*)OverlappedBuffer;
                  SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)itemex);
                  delete itemex;
                  free(OverlappedBuffer);
                  BufferSize       = 0;
                  OverlappedBuffer = NULL;
                  bOverlapped      = false;
                  iOverlappedId    = 0;
                  BufferPosition   = 0;
                  BytesRecieved    = 0;
                  itemex           = NULL;
                  uiPacketNumber   = 0;
                  break;
               }
               TAdvertiseItemUpdate *item = new TAdvertiseItemUpdate;
               ZeroMemory(item, sizeof(TAdvertiseItemUpdate));
               memcpy(item, ap.PacketData, ap.dwPacketLength);

               item->pNMHDR.code     = (*theIterator).first;
               item->pNMHDR.hwndFrom = (*theIterator).second;
               item->pNMHDR.idFrom   = ADVERTISE_SERVER;
               SendMessage((*theIterator).second, WM_NOTIFY, 0, (LPARAM)item);
               delete item;
            }
            break;
         }
      }
   }
   try {
      ResetEvent(ServerMutex);
      CloseHandle(pThis->ServerMutex);
      closesocket(ServerSocket);
      return 0;
   }
   catch(...) {
      closesocket(ServerSocket);
      return -2;
   }
}

// Here's a couple of the helper functions that do the sending...
bool CAdvertiseServer::SendAdvertisement(TAdvertiseServerPacket packet)
{
   TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
   ZeroMemory(ComputerName, sizeof(ComputerName));
   DWORD len = MAX_COMPUTERNAME_LENGTH;
   GetComputerName(ComputerName, &len);
   CString guid;
   guid.Format("%s%s", CDataAccess::DatabaseConfiguration(), ComputerName);

   strcpy(packet.GuidHash, CDataAccess::HashPassword(guid));

   bool bRetval = false;
   SOCKET s = socket(PF_INET, SOCK_DGRAM, 0);
   if(s != INVALID_SOCKET)
   {
      BOOL tru = TRUE;
      setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&tru, sizeof(tru));
      sockaddr_in sin;
      ZeroMemory(&sin, sizeof(sin));
      sin.sin_family = PF_INET;
      sin.sin_port = htons(Port);
      sin.sin_addr.s_addr = INADDR_BROADCAST;
      if(sendto(s, (char *)&packet, sizeof(packet), 0, (sockaddr *)&sin, sizeof(sin)) > 0)
      {
         bRetval = true;
         if(packet.bRequestReply)
         {
           // Here is where your work comes in, in setting up a reply, or making a TCP connection back to the other client.
         }
      }
      closesocket(s);
   }
   return bRetval;
}

bool CAdvertiseServer::Advertise(UINT uiAdvertisement, long nItemId, bool bState, void *lpBuffer, DWORD dwDataSize, bool bBroadcast)
{
   TAdvertiseServerPacket packet;
   ZeroMemory(&packet, sizeof(packet));
   TAdvertiseItemUpdateEx   item;
   ZeroMemory(&item, sizeof(item));

   UINT packetnum = 1;
   packet.bOverlappedResult = true;
   packet.bRequestReply = false;
   packet.uiAdvertisePacketType = uiAdvertisement;
   packet.dwPacketLength = sizeof(item);
   packet.uiReplyType = packetnum;
   packet.bBroadcast = bBroadcast;
   item.nItemId = nItemId;
   item.bState  = bState;
   item.dwDataSize = dwDataSize;
   memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
   packet.iPacketId = GetTickCount();
   if(SendAdvertisement(packet))
   {
      BYTE *TempBuf = new BYTE[dwDataSize];
      memcpy(TempBuf, lpBuffer, dwDataSize);

      DWORD pos = 0;
      DWORD BytesLeft = dwDataSize;
      while(BytesLeft)
      {
         TOverlappedAdvertisement item;
         packet.uiAdvertisePacketType = ADVERTISE_OVERLAPPED;
         packet.bOverlappedResult = BytesLeft > 1020;
         item.iPacketId = packet.iPacketId;
         memcpy((void*)item.Data, (void*)&TempBuf[pos], (BytesLeft >= 1020 ? 1020 : BytesLeft));
         memcpy((void*)packet.PacketData, (void*)&item, sizeof(item));
         packet.dwPacketLength = sizeof(item);
         packet.uiReplyType++;
         if(SendAdvertisement(packet))
         {
            if(BytesLeft >= 1020)
            {
               BytesLeft -= 1020;
               pos += 1020;
            }
            else
            {
               BytesLeft = 0;
            }
         }
      }
      delete TempBuf;
   }
   return true;
}

void CAdvertiseServer::Shutdown()
{
   TAdvertiseServerPacket packet;
   packet.uiAdvertisePacketType = ADVERTISE_SHUTDOWN;
   SendAdvertisement(packet);
}
于 2009-02-17T19:03:51.390 に答える
0

ここで何か見落としているかもしれませんが、プログラミング言語の選択を確認できませんでした。.Net フレームワークを使用する Windows ベースの環境では、単純な構成でセキュリティ/堅牢性を追加できる WCF を使用するのが最善の選択です。.Net 指向ではない Windows ベースのコンピューターでのソリューションが必要な場合は、これらの標準に基づいて構築された通信フレームワークである MSMQ の使用を検討します。

于 2009-02-13T17:10:11.693 に答える
0

Amazon と Microsoft はどちらも、接続され、連携している任意の数のアプリケーション間のランデブー ポイントとして使用できるキューをホストしています。Amazon は商用であり、無料ではありません。 Microsoftは現在無料ですが、永久に無料であるとは限りません。あなたが直面している問題を正確に解決します。接続されたクライアントに pub/sub モデルを提供します。

于 2009-02-13T06:43:13.760 に答える
0

分散キャッシュまたはオフライン db 機能が必要なようです - 言語 (java/c#/...) に応じて、さまざまなオプションが用意されています...

于 2008-12-24T07:38:35.870 に答える