Uhrzeit synchronisieren



  • Danke dir erstmal Shade Of Mine.

    Aber ich würde die Sync ganz gern vom Programm aus machen, also dass mein C++ Code an irgend einer stelle die Zeiten der Rechner in irgend einer Form synchronisiert.
    Einfach da wenn das mal vergessen wurde die ganze Messreihe und Auswertung nutzlos war und ich schlicht sicher sein muss das beide Rechner die gleiche Zeit haben.

    Also wie setz ich das in C++ um?



  • Gar nicht. Viel zu kompliziert; NTP ist genau die Loesung, die du haben moechtest. Die Grundlagen findest du im Wikipedia-Artikel zu NTP, aber sowas selbst zu implementieren ist Zeitverschwendung; synchronisiere einfach einen der beiden Rechner mit irgendeinem externen Zeitgeber und den anderen ueber das lokale Netz mit dem ersten.



  • Ok überzeugt, dann eben NTP. Damit hab ich genau so wenig Erfahrung.
    Ich hab es gemacht wie in dem Link von Shade Of Mine beschrieben, Schritt für Schritt.

    Ich hab dann das gefunden http://support.microsoft.com/kb/314054/de
    und das Schritt für Schritt so gemacht. Wenn ich am Ende "w32tm /resync /rediscover" ausführe steht da:

    "Neusynchronisationsbefehl wird gesendet an local computer..."
    nach einer Weile kommt dann:
    "Der Computer wurde nicht synchronisiert, da keine Zeitdaten verfügbar waren"

    unter
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
    in NtpServer hab ich die IP eingetragen von dem Rechner von dem er sich das hollen soll, in dem Fall halt 192.168.1.10

    Was mach ich falsch? Was hab ich übersehen?
    Beide Rechner sind an, beide Rechner sind im selben W-Lan-Netz. Oder muss ich noch etwas gesondert beachten, wenn sie nur über W-Lan verbuden sind?



  • Wenn du NTP verwenden willst, dann muss auf dem anderen Computer auch ein NTP Server laufen. Und auf normalen Windows Desktop PCs läuft eben keiner.
    Lässt sich aber vermutlich relativ einfach aktivieren - müsstest du dir selbst ergoogeln.

    Ich würde aber in dem von dir geschilderten Fall eher selbst synchronisieren.
    Als Clock bietet sich z.B. timeGetTime() an, mit timeBeginPeriod(1) .

    Vor der Session müsstest du dabei nur am Server die Zeit ermitteln und an den Client schicken, und dann gucken wie lange der ganze Vorgang gedauert hat. Wenns < 10ms waren, dann hast du die Zeit auf < 10ms genau synchronisiert.

    Der Client empfängt die Zeit dann, ruft sein eigenes timeGetTime() auf, und speichert die Differenz.

    Wenns mehr als 10ms waren, dann wiederholst du den ganzen Vorgang. Was du machst wenn es auch nach dem 100. mal nicht klappt musst du dann entscheiden. Entweder einfach den "besten" Wert nehmen, oder mit ner Fehlermeldung abbrechen.

    Mag etwas mehr zu programmieren sein (allerdings auch nicht sehr viel). Dafür sparst du dir beim Einrichten wieder etwas Arbeit - eben das ganze rummachen mit dem Time-Server. Was schwerer wiegt hängt davon ab wie oft das System deployed werden muss.

    Ein Vorteil an timeGetTime() ist auch, dass die Werte die du da zurückbekommst von der Systemzeit unabhängig sind. D.h. es ist egal wenn die Systemzeit verstellt wird während dein Programm gerade Daten überträgt. Und du musst dabei auch die Systemzeit nicht verstellen, da du ja nicht auf diese angewiesen bist. Beide PCs können also nach Lust und Laune verschiedene Time-Server verwenden (oder auch gar keinen).

    ps: timeBeginPeriod(1) solltest du auf jeden Fall machen. Sonst wirst du die Systemzeit nichtmal auf 10ms genau auslesen können. (Ich kann nicht garantieren dass es mit timeBeginPeriod(1) geht, aber ohne geht's auf Windows Versionen < 8 sicher nicht. Mit Windows >= 8 müsste ich es selbst erst probieren. Windows >= 8 hat nen "tickless" Kernel, da könnte es gehen.)



  • Hallo hustbaer.

    Den NTP hab ich auf dem Server eingerichtet, das war nicht das Problem. Ich hab das Problem mitlerweile gefunden. Ich hatte zwar in den Regestrie alles richtig eingestellt aber die Windows Firewall war dran schuld das es nicht ging. Jetzt wo ich auf dem Server und dem Client den Port 123 für UDP freigegeben hab kann ich nun die Client-Zeit mit dem vom Server snychronisieren. Habs erfolgreich getestet und auf dem Server die Zeit manuell verändert, sync durchgeführt und der Client hatte nun auch die Zeit.

    Zu deinem Vorschlag es selbst zu coden, bei
    "Der Client empfängt die Zeit dann, ruft sein eigenes timeGetTime() auf, und speichert die Differenz."
    liegt da nicht genau das Problem?



  • hustbaer schrieb:

    Vor der Session müsstest du dabei nur am Server die Zeit ermitteln und an den Client schicken, und dann gucken wie lange der ganze Vorgang gedauert hat. Wenns < 10ms waren, dann hast du die Zeit auf < 10ms genau synchronisiert.

    Woher weiß man, wie lange es gedauert hat?



  • @ipsec
    Sinngemäss:

    DWORD const now = ::timeGetTime(); // Zeit holen
    m_socket << now; // Zum Client schicken
    
    DWORD ack;
    m_socket >> ack; // Client schickt Acknowledge zurück
    assert(ack == 42); // Whatever
    
    DWORD const timeTaken = ::timeGetTime() - now;
    if (timeTaken <= 10)
        JoyJoyHappyHappyJoyJoy();
    

    -----

    @solidart

    solidart schrieb:

    Zu deinem Vorschlag es selbst zu coden, bei
    "Der Client empfängt die Zeit dann, ruft sein eigenes timeGetTime() auf, und speichert die Differenz."
    liegt da nicht genau das Problem?

    Welches Problem?
    Ich meine das so (Client-Code):

    DWORD g_serverTimeDifference = 0;
    
    void Sync()
    {
        // Sync-Kommando zum Server schicken
    
        // (whatever)
    
        // Server schickt Zeit zurück:
        DWORD serverTime;
        m_socket >> serverTime;
        DWORD const rawClientTime = ::timeGetTime();
        g_serverTimeDifference = rawClientTime - serverTime;
        m_socket << DWORD(42);
    }
    
    DWORD GetServerTime() // <- Funktion die die "synchronisierte" Zeit zurückliefert
    {
        return ::timeGetTime() - g_serverTimeDifference;
    }
    

    Natürlich kann es theoretisch passieren, dass der Client-Thread zwischen dem Empfangen der Daten über den Socket und dem darauffolgenden ::timeGetTime() für "längere" Zeit unterbrochen wird.
    Die Chancen dafür sind aber relativ klein. Speziell unter Windows, da ein Thread da nen kurzfristigen dynamischen Performance-Boost verpasst bekommt wenn ein IO abgeschlossen wird -- damit er schnell darauf reagieren kann.
    Wenn man möchte kann man die beiden Funktionen am Server und Client auch noch in ein SetThreadPriority-Pärchen einklammern. Auf Systemen mit >= 2 Cores sollte damit die Chance dass man unterbrochen wird ausreichend weit drücken.

    Und wenn man gänzlich paranoid ist kann man noch am Client die Zeit nehmen bevor man das "sync" Kommando zum Server schickt. Und dann checken ob der gesamte Vorgang ausreichend schnell über die Bühne gegangen ist.

    ----

    Was einen dabei noch erwischen könnte ist das "send send recv" Problem. Wenn man auf beiden Seiten für die Dauer der Synchronisation die TCP_NODELAY Option setzt sollte es aber problemlos funktionieren.
    (Und wenn Timing wichtig ist, ist zu überlegen ob man TCP_NODELAY nicht permanent gesetzt lässt -- was natürlich zu u.U. massiver Bandbreiten-Verschwendung führt.)
    Bzw. man könnte zur Synchronisierung natürlich auch gleich UDP verwenden. Wenn die beiden PCs im selben Netzwerksegment stehen müsste das ja auch gehen.



  • ps: Wenn das alles Dinge sind die einem neu sind, dann ist vielleicht doch die NTP Variante besser 😉
    Ganz einfach weil man dabei weniger falsch machen kann.

    Wobei ... auch da muss man aufpassen.
    Wenn zwischen Client und Server nur eine kleine Zeitdifferenz besteht, dann setzt Windows die Zeit beim Abgleich nicht "hart" auf die neue Zeit, sondern "schleift" die Änderung langsam ein. D.h. man hat dann ne Phase die etliche Sekunden bis vielleicht 1-2 Minuten dauert, wo die Systemzeit am Client deutlich zu schnell bzw. zu langsam läuft. Bis die Differenz eben ausgeglichen ist.

    Grundsätzlich eine tolle Sache, weil es "Sprünge" in der Systemzeit vermeidet (speziell Sprünge zurück sind für viele Anwendungen doof). Gerade wenn man die Systemzeit als genaue Zeitquelle verwenden möchte ist es aber alles andere als optimal.

    Dieses Verhalten lässt sich vielleicht auch irgendwo abschalten, aber ich hab keine Ahnung wo/wie (Registry vermutlich, aber wo?).



  • @hustbaer

    TCP_NODELAY hab ich in meinem System drin, allein schon weil der Server schneller Daten bereitstellt als sie sonst wegen des Nagle's algorithm gesendet werden und ein Paketweises senden für das System ein Nachteil ist.

    Nur damit ich dein Vorschlag richtig verstanden habe - für die Sync.
    Der Server ermittelt seine Zeit "ServerTime", sendet die an den Client, der ermittelt seine Zeit und bildet die Abweichung.
    "TimeDiff = ServerTime - ClientTime"
    Wenn ich nun später Daten mit der Zeit vom Server bekomme rechne ich die Zeit des Servers mithilfe der Differenz in die Zeit des Clients um?
    "SyncTime = ClientTime - TimeDiff"

    Mir ist noch nicht ganz klar, wie man berücksichtigt dass das W-Lan ja auch nicht beliebig schnell sendet. Und die synchronisierte Zeit sollte natürlich nicht den Delay des W-Lans enthalten.



  • solidart schrieb:

    Mir ist noch nicht ganz klar, wie man berücksichtigt dass das W-Lan ja auch nicht beliebig schnell sendet. Und die synchronisierte Zeit sollte natürlich nicht den Delay des W-Lans enthalten.

    Ich dachte es reicht wenn es < 10 ms ist.
    In dem Fall musst du ja am Server nur checken ob der gesamte Synchronisierungsvorgang in < 10 ms abgeschlossen werden konnte.

    Um die Genauigkeit noch ein wenig zu pushen kannst du die am Server ermittelte Dauer des Synchronisierungsvorgangs nochmal halbieren und zum Client schicken. Und grundsätzlich immer 5 oder 10 Versuche fahren, und dann den Durschnittswert der mittleren 50% oder so nehmen.
    (Oder einfach den Median.)


Anmelden zum Antworten