如何通过网络在私人应用程序之间进行私人通讯?

时间:2008-12-24 07:06:39

标签: networking communication private security

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

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

这种app-to-app通讯的正常/标准方式是什么?我在哪里可以找到更多信息?

此外,还有哪些技术可用于在网络上宣布和查找其他应用程序?


编辑: (提炼我的问题)

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

看起来我需要在两个或更多应用找到对方后建立P2P连接 - 我该怎么做?

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

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


编辑[09-01-06]:

我目前正在考虑以下选项:

  1. multicasting(TLDP-Howto) - 这似乎可行,但我需要进一步研究它。
  2. 使用免费的动态DNS服务器,虽然这看起来有点冒险......
  3. 使用一些免费的电子邮件工具,例如gmail / yahoo / ...,并从那里发送/读取邮件以查找其他应用程序的IP(可以工作,但感觉很脏)
  4. 已建议使用webservices,但我不知道它们是如何工作的。将不得不研究它
  5. 我很感激您对这些选项的看法以及是否有任何示例。遗憾的是,我没有选择使用中央服务器或网站(除非可以保证免费和永久)。

    [编辑2009-02-19]

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

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

12 个答案:

答案 0 :(得分:5)

请参阅Publish / Subscribe异步消息传递范例。

示例实现是Apache ActiveMQ

  

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

答案 1 :(得分:4)

答案 2 :(得分:3)

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

我在端口上设置了一个“监听器”,这是通过散列数据库服务器和应用程序连接到的数据库来选择的。这将确保我从中获得广播的任何人使用与我相同的数据库,并允许应用程序的多个实例在桌面上运行(设计要求)。

然后,我设置了各种广播某些事件的“BroadcastMessage()”函数。我甚至允许使用我的API的开发人员能够使用自定义有效负载数据创建自定义事件,然后让程序为该事件注册一个监听器,该事件会在该事件进入时通知注册器,并通过它随附的数据。

例如,当应用程序启动时,它会播放“我在这里”的消息,任何人都可以听取消息,忽略它或回复消息。在“我在这里”,它包含正在运行的应用程序的IP地址,因此任何客户端都可以通过TCP连接连接到它以进行进一步的数据更新,以便交付HAD。

我选择了UDP,因为并非所有其他正在运行的实例都能看到这些广播的要求。它比任何东西都更方便......如果有人在你在同一个屏幕上时为数据库添加了一条记录,新记录就会“出现”在你的桌面上。

如果管理员在运行应用程序时更改了用户的权限,用户也不必退出并重新进入应用程序,收到更新并在那里处理,并且用户可以做他们需要的事情。

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

答案 3 :(得分: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发现网络,它可以将地址发送给所有其他人,如果你愿意的话,这可能真的很快变大!)

答案 4 :(得分:2)

您是否希望这完全是P2P,或者您是否计划让中央服务器执行更多操作然后再作为目录?

对于通信安全性,SSL应该没问题。 Java以非常简单的方式支持这些,如果你正在使用它。这是java 6中SSL的reference

答案 5 :(得分:1)

确定。正如所承诺的,这里是我从我的应用程序中删除的一些示例代码。这不是编译和运行的预期,这是 I 如何做到这一点的一个例子。你可能不得不完全不同。另外,这是为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);
}

答案 6 :(得分: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

答案 7 :(得分:1)

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

使用的通信主体应该为您构建应用程序提供了相当坚实的基础。您只需要两个节点相互了解,然后从那里构建。我使用MonoTorrent运行一个私有(100节点)数据网络,一个RSS提要来宣告需要在哪里(Wordpress的修改版本)和在SSH隧道中执行所有操作。我有一个管理网络的中央服务器,但可以轻松地在我的100个节点中的任何一个上运行。使用动态DNS服务,如果我的服务器出现故障,第一个活动节点会将其自己的跟踪器设置为备份。

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

有很多对SSH tunneling的引用,我只是因为有趣的图表而使用这个。 SSH隧道并不是最有效的方法,但它是一个非常好的替代方法,必须以编程方式将您的通信包装在SSL隧道中。

我知道这些想法有点混乱,我希望它能帮助你指出正确的方向。 PS ...对于一个完全可移植的解决方案,您可以在Java或.Net中运行(在Mono下运行..我的AppleTV甚至运行Mono)。然后,操作系统甚至可以灵活地进行操作。

答案 8 :(得分:0)

听起来您需要分布式缓存或脱机数据库功能 - 取决于您的语言(java / c#/ ...),您可以选择各种选项...

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

答案 11 :(得分:0)

这里有关于带有WCF的P2P的好文章 http://msdn.microsoft.com/en-us/magazine/cc188685.aspx。 它提供代码,但假设.Net3,Wcf,Vista及以上