Pergunta

É basicamente um aplicativo que é instalado em múltiplos PCs, cada instalação mantendo a sua própria base de dados que é sincronizado com outros de como e quando eles são up (conectados à mesma rede) ao mesmo tempo.

Eu testei isso usando conexões de soquete simples e padrões personalizados, mas quer fazer as comms entre os aplicativos em conformidade com os padrões aceitos e também para ser seguro / robusta, e não tentar reinventar a roda.

O que é a forma normal / padrão de fazer isso comms app-a-aplicativo e onde posso encontrar mais?

Além disso, o que as técnicas são / podem ser usados ??para anunciar e encontrar os outros aplicativos em uma rede?


edit: (Refinando meu problema)

O modelo pub / sub apontado por gimel abaixo parece ser ao longo das linhas do que eu preciso. É no entanto cobre um lote de terreno e eu realmente não sei o que para tirar & uso de tudo isso.

Também parece que eu preciso para estabelecer uma conexão P2P uma vez dois ou mais aplicativos encontrado um ao outro - como posso fazer isso?

Se existem exemplos / tutoriais disponíveis, por favor apontá-los. Pequenos projetos de código aberto / módulos que implementa algo como o que eu preciso também serviria.

Meu plataforma de escolha é o Linux, mas os exemplos baseados no Windows também seria muito útil.


edição [09-01-06]:

Atualmente, estou olhando para as seguintes opções:

  1. multicasting (TLDP-Howto) - este parece viável, mas eu preciso estudá-lo um pouco mais.
  2. usando dinâmica livre servidores DNS, embora isso parece um pouco arriscado ...
  3. usando alguma facilidade e-mail gratuito, por exemplo, gmail / yahoo / ... e enviar / mail leitura de lá para encontrar o IP de outro aplicativo (pode trabalhar, mas se sente sujo)
  4. webservices tem sido sugerido, mas eu não sei como eles funcionam e terá que estudar-lo

Eu gostaria de sua opinião sobre essas opções e se existem exemplos lá fora. Eu, infelizmente, não têm a opção de usar um servidor central ou website (a menos que possa ser garantido para ser livre e permanente).

[Edit 2009-02-19]

(desejo que eu poderia aceitar dois / três respostas! O que eu já aceitei porque fornece linhas de pensamento e possibilidades, enquanto outros vieram com soluções fixas, mas aplicáveis,. Obrigado a todos que responderam, tudo isso ajuda. )

Como & quando eu encontrar e / ou implementar a minha solução, vou atualizar esta pergunta, e a solução deve ser adequada Vou criar um projeto sourceforge para ele. (É em qualquer caso, um pequeno problema dentro de um projeto muito maior.)

Foi útil?

Solução

Hmm,

Este é um pouco como um problema de matemática. A questão de como dois computadores estabelecer uma conexão uma vez que encontrar o outro é bastante simples. Você pode usar qualquer número de P2P ou protocolos cliente-servidor. SSL é quase universalmente disponível, mas você poderia também servir SSH , execute Freenet ou qualquer outra coisa. Depois de estabelecer uma conexão através de um desses protocolos, um modelo publish / subscribe para troca de dados poderia funcionar bem. Mas há

A questão de como os computadores encontrar uns aos outros é o lugar onde as coisas ficam complicadas. Existem essencialmente três casos:

  1. Todos os seus computadores estará em sua própria rede de área local. Neste caso, qualquer computador que vai em linha pode transmitir a mensagem de que é online e obter mensagem de volta a respeito do que outras máquinas estão online. Lembre-se de qualquer broadcast / multicast mensagem tem para cada máquina na rede, uma vez que não sabe o que o seu alvo. Você não pode fazer isso na internet, pois você não pode enviar uma mensagem para cada máquina na rede.

  2. Os computadores estão em nós arbitrários na internet ** e ** haverá sempre pelo menos um de seus computadores conectados à web ** e ** todos os máquina de ir on-line em uma base regular. Neste caso, cada máquina pode manter uma lista de execução de endereços IP. Quando uma máquina que tem sido desligada por um tempo voltar a ficar online, ele verifica os endereços conhecidos, ligando para o primeiro endereço válido - é assim que os servidores Localiza protocolo emule.

  3. Os computadores estão em nós arbitrários na internet ** e ** todas as máquinas off-line em conjunto ** ou ** alguns ir muito off-line por longos períodos enquanto otheers mudar endereços IP. Aqui, eu não acho que você pode demonstrar que não há caminho para novas máquinas dando em cima de encontrar um ao outro sem algum servidor central para links para IPs comuns. Não há maneira de transmitir uma mensagem em toda a Internet, porque é grande. Nestas circunstâncias, os computadores têm nenhuma informação de identificação para as outras máquinas que vêm em linha, então você precisa usar, um recurso central comum: o endereço de email que você mencionou, um site, um servidor ftp, um canal de IRC. Um DNS dinâmico é apenas mais um exemplo de um arquivo de informações centralizado. Se você deve usar tal loja de um, naturalmente, você tem que avaliar todas as lojas disponíveis para sua confiabilidade, velocidade e permanência. A menos que você dar mais informações sobre o que seu aplicativo precisa a este respeito, eu não acho que ninguém pode decidir qual loja permanente que você precisa.

Eu acho que isso cobre todas as possibilidades. O que você tem a fazer é decidir qual destes casos gerais sua aplicação cai sob e, em seguida, decidir sobre um dos protocolos que se encaixa neste caso.

Editar:

A partir de feedback, parece caso 3 se aplica. A partir do raciocínio você pode ver acima, deve ficar claro que não há nenhuma maneira de evitar algum tipo de servidor externo - a única maneira de encontrar uma agulha num palheiro é por manter o controle de onde ele é. As qualidades que gostariam de tal provedor de um são:

  • Confiabilidade: Quantas vezes é a coisa em um determinado dia
  • Velocidade: A rapidez com que a coisa respondem
  • Permanência: Quanto tempo você espera que a coisa a última? você perderá o acesso a ele como a Internet evolui?

Como mencionado, há muitos recursos facilmente disponíveis que mais ou menos se encaixam nessa conta. servidores de e-mail (como você está usando no momento), servidores web, servidores de FTP, servidores DNS, canais IRC, Twitter contas, fóruns na web ....

O problema de aplicações que vêm à vida depois de um tempo e requerematualizar sem um servidor central é um problema comum, mas comum principalmente entre criador de vírus - praticamente qualquer organização que tem os recursos para criar um aplicativo distribuído também tem recursos para manter um servidor central. Dito isto, soluções padrão ao longo dos anos incluiu e-mail, http servidores, servidores FTP, canais de IRC e servidores de DNS dinâmico. Servidores diferentes em cada uma destas categorias variam em sua velocidade, confiabilidade e permanência assim a tarefa de escolher um vai voltar para o seu julgamento. canais de IRC merecem menção, porque eles fácil e rápido de configurar, mas estes podem, de fato desaparecer à medida que a Internet evolui.

Como exemplo de aplicação de distribuição que usa uma variedade de técnicas de "finding cliente", você pode baixar a fonte para BO2K , um, uh "utilitário de administração remota". Isso pode fornecer alguns insights sobre toda a funcionalidade do seu cliente de atualização remota.

Apenas para repetir. Imagino que há três partes para o seu problema:

  1. As máquinas encontrar uns aos outros (ver acima)
  2. As máquinas estabelecer uma conexão (de novo, SSL, SSH e outros estão prontamente disponíveis)
  3. As máquinas troca de dados. Você pode usar um modelo de "publish / subscribe" ou apenas rolar o seu próprio simples protocolo . Eu trabalhei para a empresa que teve um cliente de auto-atualização e é isso que nós fizemos. As razões para a criação de seu próprio protocolo são: 1) mesmo nas situações mais simples, os requisitos para a velocidade e confiabilidade irá variar conforme vai nos dados trocados, 2. A troca simples de dados requer apenas algumas linhas de código e por isso não se preocupa seguintes protocolos para a troca de dados muito simples, 3. Uma vez que diferentes aplicativos usam diferentes métodos e linguagens, nenhum protocolo para troca de dados simples é dominante. Para situações mais complexas, há de fato uma floresta inteira de protocolos, mas sua variar complexidade seria torná-los difíceis de usar para troca de dados simples. O caminho que o git scm envia dados é um exemplo de um protocolo de actualização. Se for só acontece assim que você banco de dados se assemelha ao código fonte que git envia, você pode usar git para manter seu banco de dados. Mas as chances são de sua abordagem de atualização não lembram o que git faz isso de perto. Outro protocolo é um exemplo ou uma outra versão de serviços web tais como SOAP. Estes protocolos apenas envoltório o processo de chamar uma função em uma máquina usando XML e HTTP. Se você já pode estabelecer comunicação socket entre suas aplicações, então não há nenhuma razão para fazer isso. Lembre-se, para implementar serviços web que você precisa para executar uma corrida de um servidor HTTP e analisar o XML que um clientes HTTP em dados brutos. Considerando que você pode enviar seus dados diretamente através de um soquete, não há nenhuma razão para fazer isso. Então você está de volta para rolar seus próprios.

De qualquer forma, um exemplo de um protocolo simples poderia ser:

  • um primeiro aplicativo envia um índice da dados que tem como uma matriz de índices.
  • o outro aplicativo envia uma lista dos itens que são novos it
  • e, em seguida, o primeiro aplicativo envia os itens reais.

Em seguida, os aplicativos papéis mudam e trocar dados por outro caminho. Um dado "handshake" em seu protocolo seria parecido com isso no código pseudo:

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);
}

Com uma outra função para implementar o lado oposto dessas trocas de dados.

Uma outra consideração é que, se cada entrada no banco de dados está sendo gerado de forma independente, você terá que gerar um ID que é exclusivo para esta sem ser capaz de referenciar todos os itens no banco de dados distribuído. Você pode gerar um GUID ou apenas um grande número aleatório para esta finalidade.

Você vai, sem dúvida, tem que ajustar e desenvolver isso ainda mais se você estiver indo para usá-lo.

Tenha em mente, no entanto, que se os aplicativos são apenas ocasionalmente atualizar , não haverá maneira de estar certo de que todas as instâncias dadas terá qualquer item de dados. Por exemplo, suponha que, em um dia, metade das máquinas só ir em linha após 5:00 pm e a outra metade só vão em linha antes 5:00. Neste caso, os dois grupos de máquinas irão compartilhar nenhum dado tudo se diretamente atualizando o outro. Se, por outro lado, as máquinas realmente em às vezes até mesmo distribuídos (em vez de acordo com o padrão como eu descrevi), é razoavelmente provável que cada máquina acabará por obter todas as atualizações (pelo menos todos os antigos atualizações) .

Diante de tudo isso, eu acho que você deve considerar exatamente como raramente suas aplicações irá se conectar e como é importante para todos os dados a serem totalmente sincronizado. Em situações de extrema raridade, você pode precisar usar um servidor externo para mover seus dados, bem como encontrar seus clientes. e-mail regular é a solução natural para esse problema. Seria uma coisa para uso, se nenhum computador é online quando outro está online. Se você está preocupado com o acesso a uma determinada contas de email, você poderia começar cada aplicativo com uma lista de vários endereços de e-mail, com todos os endereços que está sendo verificado e você ter a opção de atualizar a aplicação com ainda mais endereços como dadas endereços falhar. Usando e-mail para tudo teria a virtude da simplicidade. Você pode usar

Em uma nota lado mais leve, números estações são um esforço pré-Internet para resolver o informações problema atualizar, mas eles também não se encaixam no seu caso (eles transmitido para uma grande parte do mundo, no entanto).

Outras dicas

Publish / Subscribe assíncrona de mensagens paradigma.

Um exemplo implementaion é Apache ActiveMQ :

Apache ActiveMQ é rápido, suporta muitos clientes Cruz idioma e Protocolos, vem com fácil de usar Integration Patterns Empresa e muitos recursos avançados, apoiando totalmente JMS 1.1 e J2EE 1.4.

Eu projetei um aplicativo semelhante ao que você está descrevendo vários anos atrás. Eu projetei um "Anuncie Servidor" que corria em cada área de trabalho e iria usar UDP para transmiti-lo do status a qualquer outro programa em execução na rede. Agora, isso tem seu próprio conjunto de problemas, dependendo de como você pretende executar este aplicativo ... Mas, aqui está o rápido e sujo de como funcionava ...

Eu configurar um 'ouvinte' em uma porta, que foi a escolhida por hashing o servidor de banco de dados e banco de dados o aplicativo foi conectado. Isto assegura que qualquer um que eu tenho uma transmissão a partir, estava usando o mesmo banco de dados como eu estava, e permitiu que várias instâncias do aplicativo para executar no ambiente de trabalho (exigência de projeto A).

Então, eu configurar vários "BroadcastMessage ()" funções que iria transmitir determinados eventos. Eu mesmo fui tão longe quanto os desenvolvedores autorizados usando meu API a capacidade de criar eventos personalizados, com carga de dados personalizado e, em seguida, ter o programa de registrar um ouvinte, para esse evento, que iria notificar o registrador quando o evento entrou, e passá-lo os dados que veio com ele.

Por exemplo, quando o aplicativo iniciado, ele teria transmitido um "eu estou aqui" mensagem, e ninguém escuta poderia comer a mensagem, ignorá-la ou responder a ele. No "Estou aqui", continha o endereço IP do aplicativo em execução, de modo que qualquer cliente poderia se conectar a ele através de uma conexão TCP para mais atualizações de dados, que tinha de ser entregue.

Eu escolhi UDP, porque não era um requisito que estas transmissões ser visto por todas as outras instâncias em execução. Foi mais uma conveniência do que qualquer coisa ... Se alguém tinha acrescentado um registro para o DB, enquanto você estava na mesma tela, o novo registro seria apenas 'aparecer' no seu desktop.

Ele também veio a calhar que, se um administrador alterou as permissões de um usuário enquanto eles estavam executando o aplicativo, o usuário não tem que sair e voltar a entrar no aplicativo, a atualização foi recebida e processada ali, eo usuário pode fazer o que eles precisavam.

É muito fácil de configurar um ouvinte em um segmento que escutas apenas para estes tipos de mensagens ... Se você precisa de código de exemplo, posso prever que muito, mas é em C ++, e projetado para Windows, mas ele usa o WSOCK32.LIB cru, por isso deve transferir para qualquer plataforma Unix muito fácil. (Só precisa typedef DWORD, como eu usei isso muito ..).

Eu já resolveu este problema algumas vezes sendo na gestão da rede. Sua preocupação básica parece ser "Discovery", como seus aplicativos descobrir o outro.

Honestamente a maneira mais fácil é saber o seu endereço IP e uma máscara (a maioria são de classe c), e para tentar se conectar a cada máquina em que a classe c.

Se você padrão para a classe C, o que significa que ele vai apenas sobre sempre trabalho para a maioria das redes. Você pode então permitir substituições onde você adicionar em qualquer endereços IP específicos para se conectar, ou sub-redes adicionais.

Para descobrir uma classe C, que acabou de descobrir o seu endereço de IP (digamos 192.168.2.77), então iterar sobre tudo em 192.168.2. (1-254), tentando abrir uma conexão com cada um.

Eu tenho feito isso com vários segmentos (você pode executar ping todos os dispositivos de uma vez dessa forma e ter bons resultados dentro de 3 segundos. Eu descobri uma rede classe B em como 5 minutos com algumas centenas de tópicos!), Ou você pode simplesmente ir de um para o outro em um único segmento - mas se você fizer isso Verifique se o seu tempo limite é muito baixo (1/2 um segundo ou assim), caso contrário vai levar para sempre - mesmo em 1/2 segundo ele vai tomar um minuto para fazer as rondas.

Você provavelmente também quer deixar o fio descoberta execução em segundo plano a uma velocidade menor, sempre em busca de novos companheiros.

e Cache seus "bons conhecidos" endereços IP para a inicialização mais rápida.

Não é um problema difícil, mas não é trivial quer. Apenas esperar para fazer um pouco de trabalho braçal.

Além disso, você provavelmente vai querer ser capaz de adicionar um novo IP / máscara para digitalizar uma sub-rede externa. Simplesmente não há maneira de contornar isso, se você quiser dispositivos de contato na internet (embora uma vez que se descobre PC um líquido, ele pode enviar o endereço para todos os outros, se quiser, e que poderia crescer muito grande realmente rápido!)

Você quer que este seja totalmente P2P ou você está pensando em ter um servidor central para fazer nada mais do que ser um diretório?

Para segurança das comunicações, SSL deve ser fino. Java suporta estes de uma forma bastante simples, se é isso que você está usando. aqui está a referência para SSL em java 6

Ok. Como prometido, aqui está parte do código de exemplo eu arranquei do meu aplicativo. Esta não é esperado para compilar e executar, este é um exemplo de como I o fez. Você pode ter que fazer o seu completamente diferente. Além disso, este foi escrito para Windows, e como você vai ver no código, ele usa mensagens do Windows para enviar dados através de entre o segmento do servidor e da aplicação principal, mas que tudo depende de como você pretende usá-lo. Deixei algumas das partes mais interessantes para você referência.

Quanto à parte de segurança, bem, eu acho que você pode lidar com essa parte. Isso é apenas uma simples questão de criptografar os dados antes que vá ao longo do fio, usando algum bem sabe cifra, então eu não acho que eu tinha que incluir nada disso. Por exemplo, você pode ver como eu construí os cabeçalhos dos pacotes e, em seguida, há uma carga que geralmente consiste de uma outra estrutura. Assim, criptografar essa estrutura, enviá-lo como dados, e depois decifrá-lo na outra extremidade, e copiá-lo para a estrutura adequada.

// 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);
}

Ok - assim MQ e que tipo de coisa soa como over-matar.

O meu entendimento de seu aplicativo:

Desktop aplicativo em execução em várias máquinas na mesma rede - têm seus próprios bancos de dados, necessidade de descobrir o outro.

Por que não:

1) difusão UDP / ouvir em uma base regular para "encontrar outras máquinas na mesma rede" - exemplo em Java: http://java.sun.com/docs/books/tutorial/networking/datagrams/index.html

2) soquetes Use SSL para a comunicação real após a descoberta:
http://stilius.net /java/java_ssl.php ....
http://www.exampledepot.com/egs/javax.net.ssl/Client.html

Você já pensou em usar uma configuração digitado Bittorrent?

Os diretores de comunicação utilizados devem dar-lhe uma base bastante sólida para a construção de sua aplicação. Tudo que você precisa é de dois nós para conhecer uns aos outros e, em seguida, ele constrói a partir daí. Eu uso MonoTorrent para executar uma rede de dados privada (100 nós), um feed RSS para anunciar o que arquivos precisam ser onde (versão modificada do Wordpress) e fazendo tudo em túneis SSH. Eu tenho um servidor central que gere a rede, mas que poderia facilmente ao vivo em qualquer um dos meus 100 nós. Usando um serviço de DNS dinâmico, o primeiro nó conjuntos vivos até seu próprio rastreador como um backup se o meu servidor vai para baixo.

Você pode usar arquivos XML como seu esquema de mensagens, ou modificar a transmissão da rede Bittorrent para pacotes de dados de transmissão diretamente para seu aplicativo. Eu acho que o conceito do que você está procurando está em Bittorrent. O primeiro nó ao fogo até iria recuperar a entrada DNS dinâmico ( DynDNS tem um bastante fácil de usar API ) se não houvesse um host ativo na rede. (Existe uma desvantagem ... eu correr em problemas de sincronização quando dois rastreadores de fogo até dentro da janela de TTL)

Existem muito poucas referências a SSH tunneling lá fora, eu só uso um presente, porque os diagramas de diversão. SSH tunelamento não é o método mais eficiente disponível, mas é uma alternativa muito agradável de ter que quebrar programaticamente as suas comunicações em um túnel SSL.

Eu sei os pensamentos são uma espécie de desordenados, eu só espero que lhe dá uma pequena ajuda em apontar-se na direção certa. PS ... por uma solução totalmente portátil você pode executar isso em Java ou .Net (que funciona sob Mono .. Eu tenho AppleTVs executando Mono mesmo). Então OS poderia até mesmo uma parte flexível de sua operação.

Parece que você precisa de um cache distribuído ou funcionalidade db off-line - dependendo de seu idioma (java / c # / ...), há várias opções em aberto para você ...

Amazon e Microsoft ambos têm hospedado filas que você pode usar como um ponto de encontro entre um número arbitrário de conectados, aplicações cooperantes. Amazônia é comercial, não é livre. href="http://www.microsoft.com/azure/servicebus.mspx" rel="nofollow noreferrer"> da Microsoft é atualmente livre, mas não é garantido para ser assim, para sempre. Ele resolve exatamente o problema que você está enfrentando. Fornece um modelo de pub / sub para clientes conectados.

Talvez eu tenha perdido alguma coisa aqui, mas eu não conseguia ver a sua escolha de uma linguagem de programação. Em um ambiente baseado em Windows, utilizando framework .Net, a melhor opção seria usar WCF, que permite que você adicione segurança / robustez com uma configuração simples. Se você quer uma solução com janelas com base computadores que não está orientada para .Net, gostaria de olhar para usar MSMQ, que é um quadro de comunicação construído para esses padrões.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top