Domanda

Fondamentalmente è un'app installata su più PC, ciascuna installazione mantiene il proprio database che viene sincronizzato con gli altri man mano che sono attivi (connessi alla stessa rete) contemporaneamente.

L'ho testato utilizzando semplici connessioni socket e buffer personalizzati, ma voglio che le comunicazioni tra le app siano conformi agli standard accettati e anche sicure/robuste, e non provare a reinventare la ruota.

Qual è il modo normale/standard di eseguire queste comunicazioni da app a app e dove posso trovare ulteriori informazioni?

Inoltre, quali tecniche sono/possono essere utilizzate per annunciare e trovare le altre app su una rete?


modificare:(perfezionando il mio problema)

Il modello pub/sub indicato da Gimel di seguito sembra essere in linea con ciò di cui ho bisogno.Tuttavia copre molti argomenti e non so davvero cosa togliere e utilizzare da tutto ciò.

Sembra anche che io debba stabilire una connessione P2P una volta che due o più app si trovano: come faccio?

Se sono disponibili esempi/tutorial, segnalateli.Servirebbero anche piccoli progetti/moduli open source che implementano qualcosa di simile a ciò di cui ho bisogno.

La mia piattaforma preferita è Linux, ma anche esempi basati su Windows sarebbero molto utilizzabili.


modifica [01-09-06]:

Attualmente sto valutando le seguenti opzioni:

  1. multicast (TLDP-Howto) - sembra fattibile, ma devo studiarlo ancora un po'.
  2. utilizzando server DNS dinamici gratuiti, anche se sembra un po' rischioso...
  3. utilizzando alcune funzionalità di posta elettronica gratuite, ad es.gmail/yahoo/... e invia/leggi la posta da lì per trovare gli IP di altre app (può funzionare, ma sembra sporco)
  4. sono stati suggeriti servizi web, ma non so come funzionano e dovrò studiarli

Apprezzerei la tua opinione su queste opzioni e se ci sono esempi là fuori.Sfortunatamente NON ho la possibilità di utilizzare un server o un sito web centrale (a meno che non si possa garantire che sia gratuito e permanente).

[Modifica 2009-02-19]

(Vorrei poter accettare due/tre risposte!Quello che ho accettato perché fornisce linee di pensiero e possibilità, mentre altri sono arrivati ​​con soluzioni fisse, ma applicabili.Grazie a tutti coloro che hanno risposto, tutto aiuta.)

Man mano che trovo/implementerò la mia soluzione, aggiornerò questa domanda e, se la soluzione fosse adeguata, creerò un progetto sourceforge per essa.(Si tratta in ogni caso di un piccolo problema all’interno di un progetto molto più ampio.)

È stato utile?

Soluzione

Hmm,

Questo è un po 'come un problema di matematica. La questione di come due computer stabilire una connessione , una volta che trovare l'altro è abbastanza semplice. È possibile utilizzare qualsiasi numero di protocolli P2P o client-server. SSL è quasi universalmente disponibile, ma si potrebbe anche servire SSH , eseguire Freenet o qualsiasi altra cosa. Una volta che si stabilisce una connessione attraverso uno di questi protocolli, un modello publish / subscribe per scambio di dati potrebbe funzionare bene. Ma c'è

La questione di come i computer trovare l'altro è dove le cose si fanno difficili. Ci sono essenzialmente tre casi:

  1. Tutti i computer saranno sulla propria rete locale. In questo caso, qualsiasi computer che va in linea può trasmettere un messaggio che è online e ottenere un messaggio di nuovo riguardo a ciò che le altre macchine sono in linea. Ricordate qualsiasi messaggio broadcast / multicast deve ogni macchina in rete dal momento che non sa che cosa il suo obiettivo. Non si può fare questo su internet dal momento che non è possibile inviare un messaggio a tutte le macchine in rete.

  2. I computer sono i nodi arbitrari su Internet ** e ** ci sarà sempre almeno uno dei computer collegati al web ** e ** tutte le macchine andare online su base regolare. In questo caso, ogni macchina può tenere un elenco aggiornato di indirizzi IP. Quando una macchina che è stato offline per un po 'torna in linea, controlla per gli indirizzi noti, che collega al primo indirizzo valido - questo è come il protocollo di eMule trova assistenti.

  3. I computer sono i nodi arbitrari su Internet ** e ** tutte le macchine in linea insieme ** o ** un bel po 'go offline per lunghi periodi mentre otheers passare gli indirizzi IP. Qui, non credo che si può dimostrare che non v'è alcun modo per le nuove macchine venire a trovare l'altro senza alcuni server centrale per i collegamenti con l'IPS comuni. Non c'è modo per trasmettere un messaggio attraverso Internet, perché è grande. In queste circostanze, i computer non hanno informazioni di identificazione per le altre macchine provenienti in linea quindi è necessario utilizzare un centrale, risorsa comune: l'indirizzo e-mail che hai menzionato, un sito web, un server FTP, un canale IRC. Un DNS dinamico è solo un esempio di più di un negozio di informazioni centralizzate. Se è necessario utilizzare un grande magazzino, naturalmente è necessario valutare tutti i negozi disponibili per la loro affidabilità, la velocità e la permanenza. A meno che non si danno ulteriori informazioni su ciò che la vostra applicazione ha bisogno in questo senso, non credo che chiunque altro può decidere quale archivio permanente è necessario.

Credo che questo copre tutte le possibilità. Quello che devi fare è decidere quale di questi casi generali l'applicazione rientra e poi decidere su uno dei protocolli che si inserisce questo caso.

Modifica:

Da un feedback, a quanto pare il caso 3 si applica. Dal ragionamento potete vedere sopra, dovrebbe essere chiaro che non c'è modo di evitare una qualche forma di server esterni - l'unico modo per trovare un ago in un pagliaio è quello di tenere traccia di dove si trova. Le qualità si potrebbe desiderare in un provider sono:

  • Affidabilità: Quante volte è la cosa in un dato giorno
  • Velocità: quanto tempo fa la cosa risponde
  • La permanenza: Quanto tempo si aspetta la cosa a durare? Si perde l'accesso ad esso, come Internet si evolve?

Come già detto, ci sono molte risorse disponibili facilmente che più o meno si adattano a questa proposta di legge. server di posta elettronica (come si sta attualmente utilizzando), server web, server FTP, server DNS, canali IRC, account Twitter, forum web ....

Il problema delle applicazioni che prendono vita dopo un po 'e richiedonoaggiornamento senza un server centrale è un problema comune, ma comune soprattutto tra virus writer - quasi ogni organizzazione che ha le risorse per creare un'applicazione distribuita ha anche risorse per mantenere un server centrale. Detto questo, le soluzioni standard nel corso degli anni compresi e-mail, server HTTP, server ftp, canali IRC e server DNS dinamico. Server diversi in ciascuna di queste categorie variano nella loro velocità, affidabilità e permanenza così il compito di scegliere uno risale al vostro giudizio. canali IRC meritano una menzione perché veloce e facile da impostare, ma queste potrebbero infatti svanire come Internet si evolve.

Come esempio di applicazione di distribuzione che utilizza una varietà di "cliente di trovare" le tecniche, è possibile scaricare sorgente alla BO2K , un, uh "utilità amministrazione remota". Questo potrebbe fornire qualche informazione in tutte le funzionalità del tuo cliente aggiornamento remoto.

Basta ripetere. Immagino che ci sono tre parti per il problema:

  1. Le macchine che trovano l'un l'altro (vedi sopra)
  2. Le macchine che stabiliscono una connessione (di nuovo, SSL, SSH e altri sono prontamente disponibili)
  3. Le macchine che scambiano dati. Si potrebbe utilizzare un modello "publish / subscribe" o semplicemente rotolare il proprio semplici href="http://en.wikipedia.org/wiki/Communications_protocol" protocollo . Ho lavorato per l'azienda che ha avuto un client auto-aggiornamento e questo è quello che abbiamo fatto. Le ragioni per creare il proprio protocollo sono 1) anche nelle situazioni più semplici, i requisiti di velocità e affidabilità varieranno in scambiate saranno i dati, 2. Lo scambio semplice di dati richiede poche linee di codice e quindi nessuno si preoccupa seguenti protocolli per davvero semplice scambio di dati, 3. Dal momento che così diverse applicazioni utilizzano metodi e linguaggi diversi, senza protocollo per semplice scambio di dati è dominante. Per le situazioni più complesse, lì infatti un'intera foresta di protocolli, ma la loro complessità variano li renderebbe scomodi da usare per semplice scambio di dati. Il modo in cui il GIT SCM invia dati è un esempio di un protocollo di aggiornamento. Se è il caso che si database di analogo al codice sorgente che git manda, è possibile utilizzare git per mantenere la vostra base di dati. Ma le probabilità sono il vostro approccio l'aggiornamento non sarà simile cosa git lo fa da vicino. Un altro esempio è il protocollo una o un'altra versione di come SOAP. Questi protocolli solo Scorrimento il processo di chiamare una funzione su una macchina utilizzando XML e HTTP. Se si può già stabilire la comunicazione socket tra le applicazioni, quindi non c'è motivo per farlo. Ricordate, per implementare servizi web avresti bisogno di eseguire una corsa un server HTTP e analizzare il codice XML che un client HTTP in dati grezzi. Considerando è possibile inviare i dati direttamente tramite una presa di corrente, non c'è ragione per farlo. Quindi si torna a rotolare il proprio.

In ogni caso, un esempio di un protocollo semplice potrebbe essere:

  • una prima applicazione invia un indice della dati che ha come una serie di indici.
  • l'altra applicazione invia un elenco delle voci che sono nuovi che
  • e quindi la prima applicazione invia tali elementi effettivi.

Poi le applicazioni scambiano i ruoli e scambiare dati nella direzione opposta. Un dato "stretta di mano" nel protocollo apparirebbe come questo in pseudo codice:

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

Con un'altra funzione per attuare il lato opposto di questi scambi di dati.

Una ulteriore considerazione è che se ogni voce del database viene generata in modo indipendente, è necessario generare un ID che è unico per questo senza essere in grado di fare riferimento a tutti gli elementi nel database distribuito. È possibile generare un GUID o solo un gran numero a caso per questo scopo.

È senza dubbio necessario modificare e sviluppare ulteriormente questo, se avete intenzione di usarlo.

Tenete a mente, tuttavia, che se le applicazioni sono tutti solo occasionalmente aggiornano , non ci sarà alcun modo di essere certi che qualsiasi dato le istanze avranno alcun elemento di dati. Per esempio, supponiamo, in un giorno, la metà delle macchine vanno solo on-line dopo 17:00 e l'altra metà solo andare on-line prima 05:00. In questo caso, i due gruppi di macchine condivideranno dati di sorta se l'aggiornamento direttamente tra loro. Se, d'altra parte, le macchine realmente su a volte anche distribuiti (piuttosto che in base al modello, come ho descritto), è ragionevolmente probabile che ogni macchina finirà per ottenere tutti gli aggiornamenti (almeno tutti i vecchi aggiornamenti) .

Dato tutto questo, penso che si dovrebbe considerare esattamente come raramente le applicazioni in cui si connetteranno e quanto sia importante per tutti i dati da perfettamente sincronizzati. In situazioni di estrema rarità, potrebbe essere necessario utilizzare un server esterno per spostare i dati oltre a trovare i vostri clienti. e-mail regolare è naturale soluzione a questo problema. Sarebbe una cosa da usare se nessun computer è in linea quando un altro è in linea. Se siete preoccupati per l'accesso a un particolare account di posta elettronica, si potrebbe iniziare ogni applicazione con una lista di più indirizzi email, con tutti gli indirizzi di essere controllati e si avere la possibilità di aggiornare l'applicazione con ancora più indirizzi come dato indirizzi sicuro. Utilizzando e-mail per tutto ciò che avrebbe la virtù della semplicità. Si potrebbe utilizzare

Su un lato nota più leggera, numbers station sono uno sforzo pre-Internet per risolvere il informazioni aggiornamento problema ma anche non si adatterebbe il vostro caso (essi trasmettono ad una gran parte del mondo, tuttavia).

Altri suggerimenti

publish / subscribe paradigma messaggistica asincrona.

Un esempio è implementaion Apache ActiveMQ :

  

Apache ActiveMQ è veloce, supporta molti clienti Croce Lingua e protocolli, viene fornito con facile da usare Enterprise Integration Pattern e molte caratteristiche avanzate nel pieno supporto JMS 1.1 e J2EE 1.4.

Ho progettato un'applicazione simile a quello che si sta descrivendo diversi anni fa. Ho disegnato una "Pubblicità Server" che correva su ogni desktop e vorrei utilizzare UDP per trasmettere è stati a qualsiasi altro programma in esecuzione sulla rete. Ora, questo ha il proprio insieme di questioni, a seconda di come si prevede di eseguire questa applicazione ... Ma, ecco il rapido e sporco di come ha funzionato ...

I ha installato un 'ascoltatore' su una porta, che è stato scelto da un hashing il server di database, e il database dell'applicazione era collegato a. Ciò garantirebbe che chiunque ho ricevuto una trasmissione da, stava usando lo stesso database come ero, e ha permesso più istanze di app per eseguire sul desktop (un requisito di progettazione).

Poi, I vari "broadcastMessage ()" funzioni di configurazione che avrebbe trasmesso determinati eventi. Sono anche andato per quanto riguarda gli sviluppatori autorizzati utilizzando il mio API la possibilità di creare eventi personalizzati, con carico utile di dati personalizzati, e quindi hanno il programma registrare un listener, per quell'evento, che informa il registrar quando quell'evento è entrato, e passarlo i dati che è venuto con esso.

Ad esempio, quando l'applicazione ha iniziato, sarebbe trasmettere un "Sono qui" messaggio, e chiunque poteva mangiare l'ascolto del messaggio, ignorarlo o rispondere ad esso. Nella "Sono qui", che conteneva l'indirizzo IP dell'applicazione in esecuzione, in modo che tutti i clienti potrebbe collegarsi ad esso tramite una connessione TCP per ulteriori aggiornamenti dei dati, che doveva essere consegnato.

Ho scelto UDP, perché non era un requisito che queste trasmissioni essere visti da tutti gli altri istanze in esecuzione. Era più di un comfort di ogni altra cosa ... Se qualcuno aveva aggiunto un record al DB mentre eri sullo stesso schermo, il nuovo record sarebbe solo 'appare' sul desktop.

E 'venuto anche utile che se un amministratore ha cambiato i permessi di un utente mentre erano in esecuzione l'applicazione, l'utente non ha bisogno di uscire e rientrare l'applicazione, l'aggiornamento è stato ricevuto, ed elaborato proprio lì, e la utente potrebbe fare quello che dovevano.

E 'davvero facile da installare un ascoltatore su un thread che proprio in ascolto per questi tipi di messaggi ... Se avete bisogno di codice di esempio, posso fornire anche questo, ma è in C ++, e progettato per Windows, ma utilizza il wsock32.lib cruda, quindi dovrebbe trasferire su qualsiasi piattaforma Unix abbastanza facile. (Solo bisogno di typedef DWORD, come ho usato che un sacco ..).

Ho risolto questo problema alcune volte nella gestione della rete.La tua preoccupazione di base sembra essere la "Scoperta", in che modo le tue app si scoprono a vicenda.

Onestamente il modo più semplice è conoscere il tuo indirizzo IP e una maschera (la maggior parte sono di classe c) e provare a connetterti a ciascuna macchina di quella classe c.

Se per impostazione predefinita utilizzi la classe C, ciò significa che funzionerà quasi sempre per la maggior parte delle reti.Puoi quindi consentire le sostituzioni in cui aggiungi indirizzi IP specifici a cui connetterti o sottoreti aggiuntive.

Per scoprire una classe C, devi solo capire il tuo indirizzo IP (diciamo 192.168.2.77), quindi ripetere tutto in 192.168.2.(1-254), cercando di aprire una connessione con ciascuno.

L'ho fatto con più thread (puoi eseguire il ping di tutti i dispositivi contemporaneamente in questo modo e ottenere buoni risultati entro 3 secondi.Ho scoperto una rete di classe B in circa 5 minuti con poche centinaia di thread!), oppure puoi semplicemente passare da uno all'altro in un singolo thread, ma se lo fai assicurati che il timeout sia davvero basso (1/2 un secondo o giù di lì) altrimenti ci vorrà un'eternità, anche a 1/2 secondo ci vorrà un minuto per fare il giro.

Probabilmente vorrai anche lasciare il thread di scoperta in esecuzione in background a una velocità inferiore, sempre alla ricerca di nuovi compagni.

E memorizza nella cache i tuoi indirizzi IP "conosciuti" per un avvio più rapido.

Non è un problema difficile, ma non è nemmeno banale.Aspettatevi solo di fare un po' di lavoro.

Inoltre, probabilmente vorrai essere in grado di aggiungere un nuovo IP/maschera per scansionare una sottorete esterna.Non c'è proprio modo di aggirare questo problema se vuoi contattare i dispositivi su Internet (anche se una volta che un PC scopre una rete, può inviare l'indirizzo a tutti gli altri se lo desideri, e questo potrebbe diventare molto grande molto velocemente!)

Vuoi che questo sia totalmente P2P o hai intenzione di avere un server centrale per fare qualcosa di più che poi sia una directory?

Per la sicurezza delle comunicazioni, SSL dovrebbe andare bene. Java supporta questi in un modo piuttosto semplice, se è questo che si sta utilizzando. Ecco il href="http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html" per SSL in Java 6

Ok. Come promesso, ecco un po 'del codice di esempio ho strappato dalla mia applicazione. Questo non è previsto per compilare ed eseguire, questo è un esempio di come I lo ha fatto. Potrebbe essere necessario fare il vostro completamente diverso. In più, questo è stato scritto per Windows, e, come si vedrà nel codice, che utilizza i messaggi di Windows per inviare i dati attraverso il filo tra i server e l'applicazione principale, ma che tutto dipende da come si prevede di utilizzarlo. Ho lasciato alcune delle parti più interessanti per voi di riferimento.

Per quanto riguarda la parte di sicurezza, beh, immagino si può gestire questa parte. Questo è solo una semplice questione di crittografia dei dati prima che va oltre il filo, utilizzando alcuni ben sanno cifra, quindi non pensavo che ho dovuto includere nulla di tutto ciò. Ad esempio, si può vedere come ho costruito le intestazioni dei pacchetti, e poi c'è un carico utile che di solito consiste in un'altra struttura. Così, crittografare quella struttura, inviarlo come dati, e quindi decifrare l'altra estremità, e copiarlo nella struttura adeguata.

// 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 - così MQ e che tipo di suono cosa come over-kill.

La mia comprensione della vostra applicazione:

un'applicazione desktop in esecuzione su più macchine sulla stessa rete - hanno le loro basi di dati, bisogno di scoprire l'altro.

Perché non:

1) UDP broadcast / ascolto su una base regolare per "trovare altre macchine sulla stessa rete" - ad esempio in Java: http://java.sun.com/docs/books/tutorial/networking/datagrams/index.html

2) Utilizzare socket SSL per la comunicazione reale dopo la scoperta:
http://stilius.net /java/java_ssl.php ....
http://www.exampledepot.com/egs/javax.net.ssl/Client.html

Hai pensato di usare una configurazione digitato Bittorrent?

I principi di comunicazione utilizzati dovrebbe darvi una abbastanza solida base per costruire la vostra applicazione. Tutto ciò che serve è per due nodi di conoscere l'altro e poi si costruisce da lì. Io uso MonoTorrent di eseguire un (100 nodi) della rete privata di dati, un feed RSS per annunciare quali file devono essere dove (versione modificata di Wordpress) e di fare tutto quanto in tunnel SSH. Ho un server centrale che gestisce la rete, ma che potrebbe facilmente vivere su uno qualsiasi dei miei 100 nodi. Utilizzo di un servizio di DNS dinamico, il primo nodo set vivi fino essa la propria inseguitore come backup se il mio server va giù.

È possibile utilizzare file XML come il vostro schema di messaggistica, o modificare la trasmissione della rete Bittorrent per trasmettere pacchetti di dati direttamente alla tua app. Credo che il concetto di ciò che si sta cercando è in Bittorrent. Il primo nodo al fuoco fino avrebbe rivendicare la voce DNS dinamico ( DynDNS ha una abbastanza facile da usare API ) se non ci fosse un host attivo sulla rete. (C'è un rovescio della medaglia ... che ho incontrato problemi di sincronizzazione quando due inseguitori fuoco all'interno della finestra TTL)

Ci sono alcuni riferimenti a SSH tunneling là fuori, mi basta usare questo perché dei diagrammi di divertimento. SSH tunneling non è il metodo più efficiente a disposizione, ma è una bella alternativa a dover avvolgere programatically vostre comunicazioni in un tunnel SSL.

So che i pensieri sono un po 'confuso, spero solo che ti dà un po' di aiuto nel sottolineare te stesso nella direzione giusta. PS ... per una soluzione completamente portatile è possibile eseguire questo in Java o .Net (esecuzione con Mono .. Ho AppleTVs in esecuzione Mono ancora). Poi OS potrebbe persino una parte flessibile del vostro funzionamento.

Sembra che avete bisogno di una funzionalità di cache o non in linea db distribuito - a seconda della lingua (Java / C # / ...) ci sono diverse opzioni aperte a voi ...

Amazon e Microsoft entrambi hanno ospitato le code è possibile utilizzare come un punto d'incontro tra un numero arbitrario di collegati, applicazioni cooperanti. Amazon è commerciale, non è libero. di Microsoft è attualmente gratuito, ma non garantito per essere così, per sempre. Risolve esattamente il problema che si sta affrontando. Fornisce un modello pub / sub per i clienti collegati.

Forse ho perso qualcosa qui, ma non sono riuscito a vedere la vostra scelta di un linguaggio di programmazione. In un ambiente basato su Windows, utilizzando il framework .Net, la scelta migliore sarebbe quella di utilizzare WCF, che permette di aggiungere la sicurezza / robustezza con una configurazione semplice. Se si desidera una soluzione con computer basati su Windows che non è Net oriented, vorrei considerare di usare MSMQ, che è un framework di comunicazione integrata a questi standard.

Non c'è buon articolo su P2P con WCF qui http://msdn.microsoft.com/en-us/magazine/cc188685. aspx . Esso fornisce codice, ma assume .Net3, WCF, Vista e sopra

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top