2

它基本上是一个安装在多台 PC 上的应用程序,每个安装程序都维护它自己的数据库,当它们同时启动(连接到同一网络)时,它与其他的数据库同步。

我已经使用简单的套接字连接和自定义缓冲区对此进行了测试,但希望使应用程序之间的通信符合公认的标准,并且要安全/健壮,而不是尝试重新发明轮子。

执行此应用程序到应用程序通信的正常/标准方式是什么?我在哪里可以找到更多信息?

此外,有哪些技术可以/可以用来发布和查找网络上的其他应用程序?


编辑:(细化我的问题)

下面的 gimel 指向的 pub/sub 模型似乎符合我的需要。然而,它涵盖了很多领域,我真的不知道从这一切中拿走和使用什么。

一旦两个或多个应用程序相互发现,我似乎也需要建立 P2P 连接 - 我该怎么做?

如果有可用的示例/教程,请指出。实现我需要的东西的小型开源项目/模块也可以提供服务。

我选择的平台是 Linux,但基于 Windows 的示例也非常有用。


编辑[09-01-06]:

我目前正在研究以下选项:

  1. 多播(TLDP-Howto) - 这似乎可行,但我需要进一步研究它。
  2. 使用免费的动态 DNS 服务器,虽然这看起来有点冒险......
  3. 使用一些免费的电子邮件工具,例如 gmail/yahoo/...,并从那里发送/阅读邮件以查找其他应用程序的 IP(可以工作,但感觉很脏)
  4. 有人建议使用 webservices,但我不知道它们是如何工作的,必须研究一下

我将不胜感激您对这些选项的意见,以及是否有任何示例。不幸的是,我没有选择使用中央服务器或网站(除非可以保证它是免费和永久的)。

[编辑 2009-02-19]

(希望我能接受两个/三个答案!我接受的一个是因为它提供了思路和可能性,而其他人则提供了固定但适用的解决方案。感谢所有回答的人,所有这些都有帮助。)

当我找到/实施我的解决方案时,我将更新这个问题,如果解决方案足够,我将为它创建一个 sourceforge 项目。(无论如何,在一个更大的项目中,这只是一个小问题。)

4

12 回答 12

5

请参阅发布/订阅异步消息传递范例。

一个示例实现是Apache ActiveMQ

Apache ActiveMQ 速度快,支持许多跨语言客户端和协议,带有易于使用的企业集成模式和许多高级特性,同时完全支持 JMS 1.1 和 J2EE 1.4。

于 2008-12-24T07:25:46.067 回答
4

唔,

这有点像一道数学题。两台计算机找到彼此后如何建立连接的问题相当简单。您可以使用任意数量的 P2P 或客户端-服务器协议。SSL几乎是普遍可用的,但您也可以提供SSH、运行Freenet或其他服务。一旦通过这些协议之一建立连接,用于交换数据的发布/订阅模型就可以正常工作。但是还有

计算机如何找到彼此的问题是事情变得棘手的地方。基本上分为三种情况:

  1. 您的所有计算机都将在您自己的局域网上。 在这种情况下,任何联网的计算机都可以广播一条它在线的消息,并返回有关其他计算机在线的消息。请记住,任何广播/多播消息都必须发送到网络上的每台机器,因为它不知道它的目标是什么。您无法在 Internet 上执行此操作,因为您无法向网络上的每台机器发送消息。

  2. 您的计算机位于互联网上的任意节点上**并且**总会有至少一台计算机连接到网络**并且**所有机器都会定期上线。 在这种情况下,每台机器都可以保存一个运行中的 IP 地址列表。当离线一段时间的机器重新上线时,它会检查已知地址,连接到第一个有效地址——这就是 emule 协议查找服务器的方式。

  3. 您的计算机位于 Internet 上的任意节点上 **并且** 所有计算机一起离线 ****相当多的计算机长时间离线,而其他计算机则切换 IP 地址。 在这里,我认为您无法证明没有新机器就无法找到彼此一些中央服务器链接到公共 IP。没有办法在整个 Internet 上广播消息,因为它很大。在这些情况下,计算机无法识别其他联机计算机的信息,因此您需要使用一个中央公共资源:您提到的电子邮件地址、网站、ftp 服务器、IRC 频道。动态 DNS 只是集中式信息存储的又一个实例。如果您必须使用这样的商店,您自然必须评估所有可用商店的可靠性、速度和持久性。除非您提供有关您的应用程序在这方面需要什么的更多信息,否则我认为其他人无法决定您需要哪个永久存储。

我认为这涵盖了所有可能性。您需要做的是确定您的应用程序属于这些一般情况中的哪些,然后确定适合这种情况的协议之一。

编辑:

从反馈来看,情况 3 似乎适用。从上面的推理可以看出,没有办法避免某种形式的外部服务器 - 大海捞针的唯一方法是跟踪它的位置。在这样的提供者中,人们想要的品质是:

  • 可靠性:某天发生的事情的频率
  • 速度:事物响应的速度
  • 持久性:你预计这件事会持续多久?随着互联网的发展,您会失去对它的访问权限吗?

如前所述,有许多容易获得的资源或多或少符合这个要求。电子邮件服务器(如您目前使用的)、网络服务器、FTP 服务器、DNS 服务器、IRC 频道、Twitter 帐户、网络论坛......

应用程序在一段时间后出现并且需要在没有中央服务器的情况下进行更新的问题是一个常见问题,但它主要在病毒编写者中常见 - 几乎任何有资源创建分布式应用程序的组织也有资源来维护中央服务器服务器。也就是说,多年来的标准解决方案包括电子邮件、http 服务器、ftp 服务器、IRC 通道和动态 DNS 服务器。这些类别中的不同服务器的速度、可靠性和持久性各不相同,因此选择一个的任务可以追溯到您的判断。IRC 频道值得一提,因为它们快速且易于设置,但随着互联网的发展,这些频道确实可能会逐渐消失。

作为使用各种“客户端查找”技术的分发应用程序示例,您可以将源下载到 BO2K,一个,呃“远程管理实用程序”。这可能会让您深入了解远程更新客户端的所有功能。

只是为了重复。我想您的问题分为三个部分:

  1. 机器相互发现(见上文)
  2. 建立连接的机器(同样,SSL、SSH 和其他很容易获得)
  3. 机器交换数据。您可以使用“发布/订阅”模型,或者只是推出您自己的简单协议。我为有自动更新客户端的公司工作,这就是我们所做的。创建自己的协议的原因是 1) 即使在最简单的情况下,对速度和可靠性的要求也会有所不同,交换的数据也会有所不同,2. 最简单的数据交换只需要几行代码,因此没有人费心用于真正简单数据交换的协议, 3. 由于不同的应用程序使用不同的方法和语言,因此没有用于简单数据交换的协议占主导地位。对于更复杂的情况,确实存在一整套协议,但它们的复杂性使它们难以用于简单的数据交换。git scm的方式发送数据是更新协议的一个例子。如果您的数据库恰好与 git 发送的源代码相似,您可以使用 git 来维护您的数据库。但是你的更新方法很可能不会像 git 那样做。另一个示例协议是一个或另一个版本的Web 服务,例如 SOAP。这些协议只是包装使用 xml 和 http 在一台机器上调用函数的过程。如果您已经可以在应用程序之间建立套接字通信,那么没有理由这样做。请记住,要实现 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或一个大的随机数。

如果你要使用它,你无疑必须进一步调整和开发它。

但是请记住,如果您的应用程序都只是偶尔更新,则无法确定任何给定实例都会有任何数据项。例如,假设一天中有一半的机器在下午 5:00之后才上线,另一半在下午 5:00之前才上线。在这种情况下,如果两组机器直接相互更新,它们将不会共享任何数据。另一方面,如果您的机器确实在均匀分布的时间运行(而不是按照我描述的模式),那么每台机器很可能最终将获得所有更新(至少所有旧更新) .

鉴于这一切,我认为您应该准确考虑的应用程序连接的频率以及所有数据完全同步的重要性。在极不频繁的情况下,您可能需要使用外部服务器来移动数据以及查找客户。常规电子邮件是解决此问题的自然方法。如果没有计算机在线而另一台计算机在线,那将是一回事。如果您担心访问特定的电子邮件帐户,您可以使用包含多个电子邮件地址的列表启动每个应用程序,检查所有地址,并且您可以选择使用更多地址更新应用程序,因为给定地址失败。将电子邮件用于所有事情将具有简单的优点。你可以使用

顺便说一句,数字电台是互联网前解决信息更新问题的努力,但它们也不适合您的情况(但是它们确实向世界大部分地区广播)。

于 2009-02-16T22:57:11.150 回答
3

我设计了一个与您几年前描述的类似的应用程序。我设计了一个在每个桌面上运行的“广告服务器”,并使用 UDP 将其状态广播到网络上运行的任何其他程序。现在,这有它自己的一系列问题,具体取决于您计划如何运行这个应用程序......但是,这是它工作方式的快速和肮脏......

我在一个端口上设置了一个“侦听器”,该端口是通过对数据库服务器和应用程序连接的数据库进行哈希处理来挑选的。这将确保我收到广播的任何人都使用与我相同的数据库,并允许应用程序的多个实例在桌面上运行(设计要求)。

然后,我设置了各种“BroadcastMessage()”函数来广播某些事件。我什至允许使用我的 API 的开发人员使用自定义有效负载数据创建自定义事件,然后让程序为该事件注册一个侦听器,当该事件进入时通知注册器并传递它随之而来的数据。

例如,当应用程序启动时,它会广播一条“我在这里”的消息,任何收听的人都可以吃掉这条消息,忽略它或回复它。在“我在这里”中,它包含正在运行的应用程序的 IP 地址,因此任何客户端都可以通过 TCP 连接连接到它以进行进一步的数据更新,这些数据必须被传递。

我选择了 UDP,因为所有其他正在运行的实例都不需要看到这些广播。这比什么都方便...如果有人在您在同一个屏幕上时向数据库添加了一条记录,那么新记录就会“出现”在您的桌面上。

如果管理员在运行应用程序时更改了用户的权限,用户不必退出并重新进入应用程序,更新就会被接收并在此处处理,用户可以这样做也很方便。他们需要什么。

在只侦听这些类型的消息的线程上设置侦听器真的很容易...如果您需要示例代码,我也可以提供,但它是用 C++ 编写的,专为 Windows 设计,但它使用原始 wsock32 .lib,所以它应该很容易转移到任何 Unix 平台。(只需要 typedef DWORD,因为我经常使用它..)。

于 2009-02-12T20:08:18.763 回答
3

我在网络管理中已经解决了几次这个问题。您的基本关注似乎是“发现”,您的应用程序如何相互发现。

老实说,最简单的方法是知道你的 IP 地址和掩码(大多数是 c 类),并尝试连接到 c 类中的每台机器。

如果您默认为 C 类,这意味着它几乎总是适用于大多数网络。然后,您可以在添加要连接的特定 IP 地址或其他子网的位置允许覆盖。

要发现 C 类,您只需找出您的 IP 地址(比如 192.168.2.77),然后遍历 192.168.2.(1-254) 中的所有内容,尝试打开到每个地址的连接。

我已经用多个线程完成了(你可以用这种方式一次 ping 所有设备并在 3 秒内获得良好的结果。我在 5 分钟内发现了一个 B 类网络,有几百个线程!),或者你可以去在一个线程中从一个线程到下一个线程——但如果你这样做,请确保你的超时非常低(大约 1/2 秒左右),否则它将花费很长时间——即使是 1/2 秒也需要一分钟进行巡回演出。

您可能还希望让发现线程以较低的速度在后台运行,始终寻找新的伙伴。

并缓存您的“已知良好”IP 地址以加快启动速度。

这不是一个难题,但也不是微不足道的。只是期望做一些跑腿工作。

此外,您可能希望能够添加新的 IP/掩码来扫描外部子网。如果您想联系互联网上的设备,就没有办法解决这个问题(尽管一旦一台 PC 发现了一个网络,如果您愿意,它可以将地址发送给所有其他人,而且这可能会很快变得非常大!)

于 2009-02-17T20:24:16.267 回答
2

您希望这完全是 P2P 还是您打算拥有一个中央服务器来做更多的事情而不是作为一个目录?

为了通信安全,SSL 应该没问题。Java 以一种非常简单的方式支持这些,如果你正在使用它的话。这是Java 6 中 SSL的参考

于 2008-12-24T07:45:00.223 回答
1

行。正如所承诺的,这是我从我的应用程序中提取的一些示例代码。这预计不会编译和运行,这是我如何做到的一个例子。你可能不得不做你完全不同的事情。另外,这是为 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 回答
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

您是否考虑过使用 Bittorrent 类型的设置?

使用的通信主体应该为您构建应用程序提供相当坚实的基础。您所需要的只是让两个节点相互了解,然后从那里构建。我使用MonoTorrent来运行一个私有(100 节点)数据网络、一个 RSS 提要来宣布哪些文件需要放在哪里(Wordpress 的修改版本)并在 SSH 隧道中做所有事情。我确实有一个管理网络的中央服务器,但它可以很容易地存在于我的 100 个节点中的任何一个节点上。使用动态 DNS 服务,如果我的服务器出现故障,第一个活着的节点会设置它自己的跟踪器作为备份。

您可以使用 XML 文件作为您的消息传递方案,或者修改 Bittorrent 网络的传输以将数据包直接传输到您的应用程序。我认为您正在寻找的概念在 Bittorrent 中。如果网络上没有活动主机,第一个启动的节点将回收动态 DNS 条目(DynDNS有一个相当易于使用的API )。(有一个缺点......当两个跟踪器在 TTL 窗口内启动时,我遇到了同步问题)

那里有很多关于SSH 隧道的参考资料,我只使用这个是因为有趣的图表。SSH 隧道不是最有效的可用方法,但它是一个非常好的替代方案,无需以编程方式将您的通信包装在 SSL 隧道中。

我知道这些想法有点混乱,我只是希望它能给你一点帮助,帮助你把自己指向正确的方向。PS...对于一个完全可移植的解决方案,您可以在 Java 或 .Net 中运行它(在 Mono 下运行..我有 AppleTV 甚至运行 Mono)。那么操作系统甚至可以成为您操作的灵活部分。

于 2009-02-18T18:47:31.717 回答
0

听起来您需要分布式缓存或离线数据库功能 - 根据您的语言(java/c#/...),您可以使用各种选项...

于 2008-12-24T07:38:35.870 回答
0

Amazon 和 Microsoft 都有托管队列,您可以将其用作任意数量的连接、协作应用程序之间的集合点。亚马逊是商业的,不是免费的。 微软目前是免费的,但不能保证永远免费。它完全解决了您面临的问题。为连接的客户端提供发布/订阅模型。

于 2009-02-13T06:43:13.760 回答
0

我可能在这里遗漏了一些东西,但我没有看到您选择的编程语言。在基于 Windows 的环境中,使用 .Net 框架,最好的选择是使用 WCF,它允许您通过简单的配置添加安全性/稳健性。如果你想要一个基于 Windows 的计算机的解决方案,它不是面向 .Net 的,我会考虑使用 MSMQ,它是一个根据这些标准构建的通信框架。

于 2009-02-13T17:10:11.693 回答
0

这里有一篇关于 P2P 与 WCF 的好文章 http://msdn.microsoft.com/en-us/magazine/cc188685.aspx。它提供代码,但假设 .Net3、Wcf、Vista 及更高版本

于 2009-02-18T12:45:07.080 回答