Frage

Es ist im Grunde eine Anwendung, die auf mehreren PCs installiert ist, jede Installation beibehält seine eigene Datenbank, die mit anderen synchronisierten als & wenn sie nach oben (mit dem gleichen Netzwerk) zur gleichen Zeit sind.

ich getestet habe dies mit einfachen Socket-Verbindungen und benutzerdefinierte Puffern, will aber die Comms zwischen den Anwendungen zu anerkannten Standards machen entspricht und auch sicher / robust, und nicht versuchen, zu sein, das Rad neu zu erfinden.

Was die normale / standard Art und Weise ist, dies zu tun App-to-App Comms & wo ich mehr finde heraus?

Auch, welche Techniken werden / kann verwendet werden, bekannt zu geben und die anderen Anwendungen in einem Netzwerk zu finden?


edit: (Verfeinerung mein Problem)

Das Pub / Sub-Modell, auf den gimel scheint unten entlang der Linien zu sein, was ich brauche. Es umfasst jedoch eine Menge Boden und ich weiß nicht wirklich, was wegzunehmen und benutzt das alles.

Es sieht auch wie ich eine P2P-Verbindung herstellen muß, wenn zwei oder mehr Anwendungen sie gefunden - wie kann ich das tun?

Wenn es Beispiele / Tutorien verfügbar sind, weisen Sie sie aus. Kleine Open-Source-Projekte / Module, die etwas implementiert wie das, was ich brauche, würde auch dazu dienen.

ist meine Plattform der Wahl Linux, aber Windows-basierte Beispiele wären auch sehr brauchbar sein.


edit [09-01-06]:

Ich suche derzeit auf den folgenden Optionen:

  1. Multicasting (TLDP-Howto) - dies scheint praktikabel, aber ich muss studieren es einige mehr.
  2. dynamischen DNS-Server verwenden, obwohl dies scheint ein wenig heikel ...
  3. mit einiger kostenlosen E-Mail-Anlage, zum Beispiel gmail / yahoo / ... und senden / Lese Mail von dort anderem App-IPs zu finden (kann funktionieren, aber fühlt sich schmutzig)
  4. Web Service wurde vorgeschlagen, aber ich weiß nicht, wie sie funktionieren und wird es studieren müssen

Ich würde Ihre Meinung zu diesen Optionen zu schätzen wissen, und wenn es Beispiele gibt es aus. Ich habe leider nicht die Möglichkeit, einen zentralen Server oder Website (es sei denn, es garantiert werden kann, frei und dauerhaft sein).

[Bearbeiten 2009-02-19]

(Ich wünsche ich zwei / drei Antworten annehmen könnte! Die, die ich angenommen habe, weil es Gedankenlinien und Möglichkeiten bietet, während andere mit festem kamen, aber falls Lösungen. Dank an alle, die beantwortet sind alle, es hilft. )

Wie und wann ich find / implementieren meine Lösung, werde ich diese Frage aktualisieren, und sollte die Lösung sein, angemessen Ich werde ein Source Projekt für sie erstellen. (Es ist in jedem Fall ein kleines Problem in einem weit größeren Projekt.)

War es hilfreich?

Lösung

Hmm,

Das ist ein bisschen wie ein mathematisches Problem. Die Frage, wie zwei Computer schafft eine Verbindung , wenn sie sich zu finden, ist relativ einfach. Sie können eine beliebige Anzahl von P2P oder Client-Server-Protokolle verwenden. SSL ist fast überall verfügbar, aber man könnte auch a href = "http dienen <: // www. openssh.com/“rel = "nofollow noreferrer"> SSH , führen Freenet oder was auch immer. Sobald Sie eine Verbindung über eines dieser Protokolle schaffen, einen Publish / Subscribe-Modell für Austausch von Daten könnte gut funktionieren. Aber es gibt

Die Frage, wie die Computer sie zu finden ist, wo die Dinge schwierig werden. Im Wesentlichen gibt es drei Fälle:

  1. Alle Computer in Ihrem eigenen lokalen Netzwerk sein wird. In diesem Fall, dass jeder Computer online geht, kann eine Nachricht senden, dass es online ist und die Nachricht über zurückbekommen, was andere Maschinen online sind. Denken Sie daran, jede Broadcast / Multicast-Nachricht auf dem Netzwerk zu jeder Maschine hat, da es nicht das, was sein Ziel nicht kennt. Sie können dies im Internet nicht tun, da Sie nicht eine Nachricht an jede Maschine im Netz senden können.

  2. Ihr Computer auf beliebigen Knoten im Internet sind ** und ** es wird immer mindestens einer der Computer mit dem Internet verbunden ** und ** alle Maschine online gehen, auf einer regelmäßigen Basis. In diesem Fall kann jede Maschine eine laufende Liste von IP-Adressen halten. Wenn eine Maschine, die für eine Weile offline gewesen ist wieder online geschaltet wird, überprüft es für die bekannten Adressen, an der ersten gültigen Adresse verbindet - das ist, wie das emule-Protokoll findet Servern.

  3. Ihr Computer auf beliebigen Knoten im Internet ist ** und ** alle offline zusammen Maschinen ** oder ** schon einige gehen offline für längere Zeit während otheers IP-Adressen wechseln. Hier, ich glaube nicht, dass Sie nachweisen können, dass es keine Möglichkeit für neue Maschinen kommen auf einander finden ohne einige zentralen Server Links zu gemeinsamen IPs. Es gibt keine Möglichkeit, eine Nachricht im ganzen Internet zu übertragen, weil es groß ist. Unter diesen Umständen haben Computer keine identifizierenden Informationen für die anderen Maschinen online geschaltet, so dass Sie eine zentrale, gemeinsame Ressource verwenden müssen: die E-Mail-Adresse, die Sie erwähnten, eine Website, einen FTP-Server, einen IRC-Channel. Ein dynamisches DNS ist nur ein weiteres Beispiel eines zentralen Informationsspeichers. Wenn Sie einen solchen Speicher verwenden müssen, natürlich müssen Sie alle Geschäfte für ihre Zuverlässigkeit, Geschwindigkeit und Dauerhaftigkeit zur Verfügung zu bewerten. Sofern Sie weitere Informationen zu geben, was Ihre Anwendung in dieser Hinsicht braucht, glaube ich nicht, dass jemand anders entscheiden kann, welche Sie permanente Speicher benötigen.

Ich denke, das deckt alle Möglichkeiten. Was Sie tun müssen, ist zu entscheiden, welche dieser allgemeinen Fällen Ihre Anwendung fällt unter und dann entscheiden, auf einem der Protokolle, die diesen Fall passt.

Edit:

Von Feedback, so scheint es, Fall 3 gilt. Aus der Argumentation Sie oben sehen können, sollte es klar sein, dass es keine Möglichkeit gibt, irgendeine Form von einem externen Server zu vermeiden - der einzige Weg, eine Nadel im Heuhaufen zu finden, ist die Verfolgung von wo es ist. Die Qualitäten, die man in einem solchen Anbieter möchte, sind:

  • Zuverlässigkeit: Wie oft ist das Ding in einem bestimmten Tag
  • Geschwindigkeit: Wie schnell die Sache reagiert
  • Permanence: Wie lange wollen Sie die Sache an? Werden Sie Zugang zu ihm wie das Internet entwickelt sich verlieren?

Wie bereits erwähnt, gibt es viele leicht verfügbare Ressourcen, die mehr oder weniger diese Rechnung passen. E-Mail-Server (wie Sie derzeit verwenden), Web-Server, FTP-Server, DNS-Server, IRC-Kanäle, Twitter-Konten, Web-Foren ....

Das Problem der Anwendungen, die nach einer gewissen Zeit zum Leben erweckt wird und erfordernohne einen zentralen Server zu aktualisieren ist ein häufiges Problem, aber es üblich, vor allem unter dem Virenschreiber - fast jede Organisation, die die Ressourcen eine verteilten Anwendung auch Ressourcen, um einen zentralen Server zu halten hat zu erstellen hat. Das sei gesagt, Standardlösungen im Laufe der Jahre inklusive E-Mail, HTTP-Server, FTP-Server, IRC-Kanälen und dynamischen DNS-Server. Verschiedene Server in jeder dieser Kategorien unterscheiden sich in ihrer Geschwindigkeit, Zuverlässigkeit und Dauerhaftigkeit so dass die Aufgabe eine der Wahl auf Ihr Urteil geht zurück. IRC-Kanäle verdienen Erwähnung, weil sie schnell und einfach einzurichten, aber diese könnte in der Tat verblassen, wie das Internet entwickelt.

Als Beispiel für Verteilungsanwendung, die eine Vielzahl von Techniken verwendet „Kunden zu finden“, können Sie herunterladen die Quelle BO2K , eine, äh "Remote-Administration Utility". Dies könnte einen Einblick in die gesamte Funktionalität der Remote-Aktualisierung Client zur Verfügung stellen.

Just zu wiederholen. Ich stelle mir vor es gibt drei Teile für Ihr Problem:

  1. Die Maschinen einander (siehe oben)
  2. finden
  3. Die Maschinen Verbindungsaufbau (wieder, SSL, SSH und andere sind leicht verfügbar)
  4. Die Maschinen Austausch von Daten. Sie könnten eine „Publish / Subscribe-Modell“ oder einfach nur Ihre eigenen einfachen Protokoll rollen. Ich arbeitete für Unternehmen, die eine Auto-Aktualisierung Kunde hatte und das ist, was wir getan haben. Die Gründe für ein eigenes Protokoll zu schaffen, sind 1) selbst in den einfachsten Situationen, die Anforderungen an die Geschwindigkeit und Zuverlässigkeit variiert ebenso wie die Daten ausgetauscht werden, 2. Die einfachste Datenaustausch erfordert nur ein paar Zeilen Code und so niemand stört folgende Protokolle für die wirklich einfachen Datenaustausch, 3. Da so unterschiedliche Anwendungen unterschiedliche Methoden und Sprachen verwenden, ist kein Protokoll für den einfachen Datenaustausch dominant. Für komplexere Situationen gibt in der Tat einen ganzen Wald von Protokollen aber ihre Komplexität variieren würde sie umständlich machen für einen einfachen Datenaustausch zu verwenden. Die Art und Weise, dass die git scm Daten sendet, ist ein Beispiel eines Aktualisierungsprotokolls. Wenn es passiert einfach so, dass Sie Datenbank den Quellcode ähnelt, die git sendet, könnten Sie git benutzen, um Ihre Datenbank zu pflegen. Aber die Chancen sind Ihre Aktualisierung Ansatz wird nicht ähneln, was git tut das eng. Ein weiteres Beispiel Protokoll ist die eine oder andere Version von Webdiensten wie SOAP. Diese Protokolle nur wickeln der Prozess der Aufruf einer Funktion auf einer Maschine xml und http. Wenn Sie bereits Socket-Kommunikation zwischen Anwendungen aufbauen können, dann gibt es keinen Grund, dies zu tun. Denken Sie daran, zu implementieren Web-Services benötigen Sie hätte gerne einen Lauf einen HTTP-Server laufen zu lassen und analysieren die XML, die eine HTTP-Clients in Rohdaten. Erwägen Sie Ihre Daten direkt über eine Socket senden kann, gibt es keinen Grund, dies zu tun. Sie sind also wieder Ihre eigenen Rollen.

Wie auch immer, ein Beispiel für ein einfaches Protokoll könnte sein:

  • eine App sendet zuerst einen Index der Daten, die er hat als eine Anordnung von Indizes.
  • die andere App sendet eine Liste der Elemente, die neu sind es
  • und dann die erste App sendet diese tatsächlichen Einzelteile.

Dann ändern sich die Apps Rollen und tauschen Daten in die andere Richtung. Ein gegebenes „Handshake“ in Ihrem Protokoll wie dies in Pseudo-Code aussehen:

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

Mit einer weiteren Funktion der gegenüberliegende Seite dieses Datenaustausches zu implementieren.

Eine weitere Überlegung ist, dass, wenn jeder Eintrag in der Datenbank unabhängig erzeugt wird, müssen Sie eine ID erzeugen, die ohne in der Lage zu diesem einzigartigen alle Elementen in der verteilten Datenbank zu verweisen. //en.wikipedia: Sie können ein GUID oder nur eine große Zufallszahl für diesen Zweck.

Sie werden ohne Zweifel haben zu optimieren und diese weiter zu entwickeln, wenn Sie es verwenden werden.

Beachten Sie jedoch, dass , wenn Ihre Anwendungen alle nur gelegentlich zu aktualisieren , gibt es keinen Weg, um sicher zu sein, dass eine bestimmte Instanzen jede Datenelement haben. Beispiel: Angenommen, an einem Tag, die Hälfte der Maschinen nur online gehen, nach 05.00 und die andere Hälfte nur online gehen, vor 05.00. In diesem Fall werden die beiden Gruppen von Maschinen überhaupt keine Daten gemeinsam nutzen, wenn sie sich direkt zu aktualisieren. Wenn auf der anderen Seite, Ihre Maschinen tun auf sogar bei verteilten Zeiten wirklich (und nicht nach Muster, wie ich beschrieben), ist es einigermaßen wahrscheinlich, dass jede Maschine schließlich alle Updates (zumindest all alten Updates) erhalten wird .

In Anbetracht all dies, ich glaube, Sie genau prüfen sollte wie selten Ihre Anwendungen eine Verbindung herstellen, und wie wichtig es ist, alle Daten vollständig synchronisiert werden. In Situationen extremen Seltenheit, müssen Sie einen externen Server verwenden, um Ihre Daten sowie die Suche nach Ihren Kunden zu bewegen. Regelmäßige E-Mail ist natürliche Lösung für dieses Problem. Es wäre eine Sache zu verwenden, wenn kein Computer immer online, wenn eine andere online. Wenn Sie sich Sorgen über den Zugriff auf ein bestimmtes E-Mail-Konten sind, können Sie jede Anwendung mit einer Liste von mehreren E-Mail-Adressen beginnen, wobei alle Adressen überprüft werden und mit Ihnen die Möglichkeit, die Anwendung mit noch mehr Adressen zu aktualisieren, wie angegebene Adressen fehlschlagen. Mit E-Mail für alles würde die Tugend der Einfachheit. Sie könnten verwenden

Auf einer leichteren Seite beachten, Zahlen Stationen ein Pre-Internet Aufwand sind zu lösen Informationen zu aktualisieren Problem, aber sie würde auch nicht passen Ihren Fall (sie zu einem großen Teil der Welt sie ausgestrahlt, jedoch).

Andere Tipps

Siehe Publish / Subscribe asynchrones Messaging Paradigma.

Ein Beispiel implementaion ist Apache ActiveMQ :

  

Apache ActiveMQ schnell ist, unterstützt viele Cross Language-Clients und Protokolle, kommt mit leicht Enterprise Integration Patterns zu verwenden und viele erweiterte Funktionen, während vollständig JMS 1.1 und J2EE 1.4 unterstützen.

habe ich eine ähnliche App zu dem, was man vor einigen Jahren beschreiben. Ich entwarf eine „Werbung Server“, die auf jedem Desktop lief, und würde UDP verwenden es ist Status für alle anderen Programme auszustrahlen im Netzwerk ausgeführt wird. Nun, dies hat seine eigenen Probleme, je nachdem, wie Sie diese Anwendung auszuführen ... Aber hier ist die schnell und schmutzig, wie es funktioniert ...

ich ein Setup ‚Hörer‘ auf einem Port, die durch Hashing der Datenbankserver eine ausgewählt wurde, und die Datenbank wurde die Anwendung verbunden ist. Dies würde sicherstellen, dass jeder bekam ich eine Sendung aus, war die gleiche Datenbank wie ich war, und erlaubt mehrere Instanzen der App auf dem Desktop (A Design-Anforderung).

laufen

Dann habe ich Setup verschiedene „Broadcast ()“ Funktionen, die bestimmte Ereignisse würden ausgestrahlt. Ich ging sogar so weit, erlaubt Entwicklern meine API die Möglichkeit, benutzerdefinierte Ereignisse zu erstellen, mit eigenen Nutzdaten, und haben dann das Programm einen Listener registrieren, für diese Veranstaltung, die die Register- würde, wenn das Ereignis eintrat, und übergeben es die Daten, die mit ihm kamen.

Zum Beispiel, wenn die App gestartet wird, wäre es ausgestrahlt ein „Ich bin hier“ -Meldung, und jemand hört die Nachricht essen konnte, ignorieren oder darauf antworten. In der „Ich bin hier“, er die IP-Adresse der laufenden Anwendung enthalten ist, so dass alle Kunden, um es über eine TCP-Verbindung für die weiteren Datenaktualisierungen herstellen können, die geliefert werden mußten.

Ich entschied mich für UDP, weil es nicht erforderlich war, dass diese Sendungen von allen anderen laufenden Instanzen zu sehen. Es war mehr Bequemlichkeit als alles ... Wenn jemand einen Datensatz in die DB hinzugefügt hatte, während man auf dem gleichen Bildschirm war, der neue Datensatz würde nur auf dem Desktop ‚erscheinen‘.

Es kam auch nützlich, wenn ein Administrator Berechtigungen eines Benutzers geändert wird, während sie die App ausgeführt wurden, kann der Benutzer musste nicht verlassen und wieder in den App wurde das Update erhalten, und genau dort verarbeitet und die Benutzer könnten das tun, was sie brauchten, um.

Es ist wirklich einfach ein Zuhörer auf einem Thread zu Setup, das für diese Art von Nachrichten nur zuhört ... Wenn Sie Beispiel-Code, kann ich das auch schaffen, aber es ist in C ++, und für Windows entwickelt, aber es nutzt die rohe WSOCK32.LIB, so dass es zu einer Unix-Plattform sehr einfach übertragen über soll. (Nur muß DWORD typedef, wie ich, dass eine Menge verwendet, ..).

Ich habe dieses Problem ein paar Mal in Netzwerk-Management ist gelöst. Ihr Grundanliegen scheint „Discovery“ zu sein, wie Sie Ihre Anwendungen entdecken einander.

Ehrlich gesagt ist der einfachste Weg ist Ihre IP-Adresse und eine Maske zu wissen (die meisten sind Klasse c), und zu versuchen, jede Maschine in dieser Klasse c zu verbinden.

Wenn Sie in die Klasse C Standard, das bedeutet, dass es werde nur über immer für die meisten Netzwerke arbeiten. Sie können dann überschreibt erlauben, wo Sie in einer bestimmten IP-Adressen hinzufügen oder weitere Subnetze zu verbinden.

eine Klasse C zu entdecken, die Sie gerade Ihre IP-Adresse herausfinden (sie 192.168.2.77 sagen), dann über alles iterieren in 192.168.2. (1-254), um eine Verbindung mit jedem zu öffnen versuchen.

Ich habe es getan mit mehreren Threads (Sie können auf einmal diese Weise alle Geräte ping und haben gute Ergebnisse innerhalb von 3 Sekunden. Ich eine Klasse-B-Netz in wie 5 Minuten mit ein paar hundert Fäden entdeckt!), Oder Sie gehen können von einem zum nächsten in einem einzigen Thread - aber wenn Sie das tun sicherstellen, dass Ihr Timeout wirklich niedrig ist (1/2 eine Sekunde oder so), ansonsten es ewig dauern wird - auch bei 1/2 Sekunde wird es nimmt eine Minute, um die Runde zu machen.

Sie wollen wahrscheinlich auch die Entdeckung Thread verlassen mit einer geringeren Geschwindigkeit im Hintergrund ausgeführt wird, immer nach neuen Kumpels zu suchen.

Und den Cache "Bekannte gut" IP-Adressen für einen schnelleren Start.

Es ist nicht ein schwieriges Problem, aber es ist auch nicht trivial. erwartet nur eine wenig Beinarbeit zu tun.

Auch mögen Sie wahrscheinlich einen neuen IP / Maske hinzufügen zu können, ein externes Subnetz zu scannen. Es gibt einfach keine Möglichkeit, dies zu umgehen, wenn Sie Geräte über das Internet kontaktieren wollen (obwohl einmal ein PC ein Netz entdeckt, kann er die Adresse an alle anderen senden, wenn Sie wollen, und das ist wirklich groß wirklich schnell wachsen könnte!)

Haben Sie diese völlig P2P sein wollen oder planen Sie für einen zentralen Server auf, die etwas zu tun, mehr als ein Verzeichnis zu sein?

Für die Kommunikation Sicherheit sollten SSL in Ordnung sein. Java unterstützt diese in einem ziemlich einfachen Weg, wenn es das ist, was Sie verwenden. hier ist die Referenz für SSL rel="nofollow in Java 6

Ok. Wie versprochen, hier ist ein Code der Probe ich aus meiner Anwendung gerissen. Dies ist nicht kompilieren und ausführen zu erwarten, ist dies ein Beispiel dafür, wie I es tat. Sie können Ihr ganz andere zu tun. Außerdem wurde diese für Windows geschrieben, und wie Sie im Code sehen werden, verwendet es Windows-Nachrichten Daten über zwischen dem Server-Thread zu senden und der Hauptanwendung, aber das hängt alles davon ab, wie Sie es zu benutzen. Ich ließ einige der interessanteren Teile für Sie zu verweisen.

Wie für die Sicherheit Teil, na ja, ich meine, Sie, dass ein Teil umgehen können. Das ist nur eine einfache Frage der Verschlüsselung der Daten, bevor sie über den Draht geht, Chiffre einige gut kennen verwenden, so dass ich nicht glaube, ich irgendetwas davon gehören musste. Zum Beispiel können Sie sehen, wie ich die Paket-Header konstruiert, und dann gibt es eine Nutzlast, die in der Regel aus einer anderen Struktur besteht. So verschlüsseln, die Struktur, als Daten senden und entschlüsseln es dann am anderen Ende, und kopieren Sie sie in die richtige Struktur.

// 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 - so MQ und diese Art von Sache klingt wie über-kill.

Ihr Verständnis für Ihren App:

Desktop-Anwendung auf mehreren Rechnern im gleichen Netzwerk laufen - haben ihre eigenen Datenbanken, müssen sich gegenseitig entdecken.

Warum nicht:

1) UDP-Broadcast / hören auf einer regelmäßigen Basis zu "anderen Maschinen auf dem gleichen Netzwerk finden" - Beispiel in Java: http://java.sun.com/docs/books/tutorial/networking/datagrams/index.html

2) Verwenden Sie SSL-Sockets für die eigentliche Kommunikation nach Entdeckung:
http://stilius.net /java/java_ssl.php ....
http://www.exampledepot.com/egs/javax.net.ssl/Client.html

Haben Sie darüber nachgedacht einen Bittorrent getippt Setup mit?

Die verwendeten Kommunikationsprinzipien sollten Sie eine ziemlich solide Basis geben für Ihre Anwendung zu bauen. Alles, was Sie brauchen, ist für zwei Knoten umeinander zu wissen, und es baut dann von dort aus. Ich benutze MonoTorrent ein privates (100 Knoten) Datennetz zu laufen, ein RSS-Feed zu verkünden, welche Dateien sein müssen wo (modifizierte Version von Wordpress) und alles in SSH-Tunnel zu tun. Ich habe einen zentralen Server, die das Netzwerk verwaltet, aber das leicht auf einen meiner 100 Knoten leben konnte. Mit Hilfe eines dynamischen DNS-Dienst, der erste Knoten am Leben setzt seinen eigenen Tracker als Backup, wenn mein Server ausfällt.

Sie können XML-Dateien als Messagingschema verwenden, oder die Übertragung des Bittorrent-Netzwerkes ändern, um Datenpakete direkt zu Ihren App zu übertragen. Ich denke, das Konzept von dem, was Sie suchen in Bittorrent ist. Der erste Knoten feuern würde die dynamische DNS-Eintrag zurückzufordern ( DynDNS eine ziemlich einfache API ), wenn es keine aktive Host im Netzwerk ist. (Es gibt einen Nachteil ... Ich habe in sync Probleme ausgeführt werden, wenn zwei Tracker im TTL-Fenster feuern)

Es gibt einige Hinweise auf SSH-Tunneling da draußen, ich habe gerade diese benutzen, weil der Spaß-Diagramme. SSH-Tunneling ist nicht die effizienteste Methode zur Verfügung, aber es ist eine sehr gute Alternative zu haben programmatisch Ihre Kommunikation in einem SSL-Tunnel zu wickeln.

Ich weiß, dass die Gedanken Art von jumbled sind, ich hoffe nur, es Ihnen ein wenig Hilfe gibt, sich in die richtige Richtung in zeigen. PS ... für eine vollständig tragbare Lösung, die Sie dies in Java oder .Net laufen konnten (unter Mono laufen .. Ich habe AppleTVs Mono läuft gerade). Dann OS könnte sogar ein flexibles Teil Ihres Betriebs.

Klingt wie Sie eine verteilten Cache oder offline db Funktionalität benötigen - je nach Sprache (Java / C # / ...) gibt es verschiedene Möglichkeiten offen ...

Amazon und Microsoft haben beide Warteschlangen gehosteten Sie als Treffpunkt zwischen einer beliebigen Anzahl von angeschlossenen, kooperierenden Anwendungen verwenden können. Amazon ist kommerziell, nicht frei. Microsofts ist derzeit kostenlos, aber nicht so für immer sein garantiert. Es löst genau das Problem Sie konfrontiert sind. Bietet ein Pub / Sub-Modell für die angeschlossenen Clients.

Vielleicht habe ich hier etwas verpasst, aber ich nicht Ihre Wahl einer Programmiersprache zu sehen. In einer Windows-basierten Umgebung mit .NET Framework, wäre die beste Wahl WCF zu verwenden, die Sie Sicherheit / Robustheit mit einer einfachen Konfiguration hinzufügen kann. Wenn Sie eine Lösung mit Windows-basierten Computern wollen, die nicht .NET orientiert ist, würde ich schauen in MSMQ verwenden, die ein Kommunikationsrahmen zu diesen Standards gebaut ist.

Es gibt gute Artikel über P2P mit WCF hier http://msdn.microsoft.com/en-us/magazine/cc188685. aspx . Es bietet Code, sondern geht davon aus .Net3, Wcf, Vista und oben

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top