J o: Nachtrag: Zeitdifferenz

Guten Abend,

ich habe endlich wieder etwas Zeit und kann mich noch einmal um dieses Problem kümmern. Leider kann ich darauf nicht mehr Antworten, darum ein neuer Beitrag.

Kurz zusammengefasst: Zwischen meinem Server (Node.js) und den angebundenen Clienten kommt es zu unterschiedlichen Zeiten wenn diese mit new Date().getTime() angefordert werden. Soweit so logisch. Nun suche ich nach einer Möglichkeit, das diese Zeiten (zumindest rechnerisch) synchron laufen.

Dazu habe ich damals vorläufig ein kleines Workaround gebastelt, bei dem der Server seine Zeit alle 10ms zu allen Clients schickt. Dies sollte jedoch schon da keine Dauerlösung sein.

Client:

socket.on( 'setServerTime', function( res ) {
	now = res / 1000;
});

res entspricht dem new Date().getTime() welches auf dem Server aufgerufen wird.

Dies habe ich einmal kurz erweitert um zu schauen ob der unterschied zwischen Client und Server konstant ist.

socket.on( 'setServerTime', function( res ) {
	console.log('');
	now = res / 1000;
	console.log(new Date().getTime()/1000 - now);
});

Wie sich herausstellt ist nicht einmal die Differenz konstant. Ich habe allerdings die Vermutung das dies mehr von der Zeit abhängt welche console.log() benötigt.

Nun arbeitet mein WebGL eigentlich ausschließlich mit performance.now(). ( Was ich auch noch ändern muss, da das Angezeigte auch von der jetzigen Serverzeit abhängt )

Nun hatte ich die Idee, das ich auf die Serverzeit, welche einmalig auf Anfrage bei dem Server zurückkommen kann, einfach performance.now() addieren könnte.

Dazu finde ich aber keine Möglichkeit performance auf den Wert 0 zu setzen, in dem Moment wenn die Serverzeit zum Client kommt. Außerdem weiß ich nicht wieviel Zeit vergeht zwischen 1. Server ruft seine Zeit ab und 2. Client erhält diese Zeit. Außerdem ist nach meinem Empfinden ein Konstrukt wie:

var diff, start;
socket.on( 'event', function( res ) {
	diff = performance.now();
	start = res / 1000;
	
});

// Event braucht die jetzige Serverzeit:
var now = start + performance.now() - diff;

auch eher grenzwertig.

Eine Überprüfung wie performance funktioniert ist auch schwierig, da logischerweise [native code].

Also wie bringe ich dem Client möglichst genau bei welche Zeit der Server hat?

Gruß
Jo

  1. hallo

    Also wie bringe ich dem Client möglichst genau bei welche Zeit der Server hat?

    bringe sie auf dem gleichen board unter.

    1. Hey,

      Also wie bringe ich dem Client möglichst genau bei welche Zeit der Server hat?

      bringe sie auf dem gleichen board unter.

      Das würde ich gern sehen wie du das realisieren möchtest.

      Gruß
      Jo

  2. Die Laufzeit vom Client zum Server ist doch rauszukriegen oder!? Also der Client schickt was zum Server und ermittelt die Zeit bis zum Eintreffen der Response.

    1. Hallo,

      dazu müssen aber Senden und Empfangen (annähernd) gleich schnell ablaufen.

      Gruß
      Jürgen

      1. Ja natürlich, da spielt die Bandbreite rein. Man kann jedoch infolge Vergleichsmessungen den Fehler hinreichend genau bestimmen. Das Netzwerktool pathchar arbeitet mit einem solchen Algorithmus.

    2. Die Laufzeit vom Client zum Server ist doch rauszukriegen oder!? Also der Client schickt was zum Server und ermittelt die Zeit bis zum Eintreffen der Response.

      Wirklich? Ich glaube nicht. Beide Zeiten sind ja unterschiedlich.

      Also angenommen die Zeit einer Anfrage beträgt zwischen Client und Server 50ms. Und der Client läuft der Serverzeit um 500ms nach. Dann schickt der Client etwas um 1542130669.100 los ( beim Server ist es zu diesem Zeitpunkt 1542130669.600 ), der Server erhält nach seiner Uhr also die Anfrage um 1542130669.650 und die Anfrage hat für den Server 550ms gedauert.

      Man könnte wohl nur etwas vom Server wieder zum Client zurück schicken und dann annehmen, dass die Zeit einer Anfrage gerade die Hälfte der vergangenen Zeit auf dem Client ist. Aber ob das immer korrekt ist wage ich auch zu bezweifeln. Zudem weiß der Client dann immer noch nicht wie groß die Differenz zu der Serverzeit ist.

      Gruß
      Jo

    3. @@pl

      Die Laufzeit vom Client zum Server ist doch rauszukriegen oder!?

      Und dabei sollte man herausfinden, dass die Laufzeit alles andere als konstant ist.

      LLAP 🖖

      --
      „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
      1. @Gunnar Bittersmann

        Die Laufzeit vom Client zum Server ist doch rauszukriegen oder!?

        Und dabei sollte man herausfinden, dass die Laufzeit alles andere als konstant ist.

        So wird man sicher auch herausfinden wie groß die Abweichungen sind! Dafür gibt es sogar mathematische Methoden wie partielle Differentation.

        MfG

  3. Hallo Jo,

    ich habe es mir nicht genauer angeschaut, aber ich weiß, dass es für die Synchronisierung von Computern in paketvermittelten Netzen wie IP das Protokoll NTP gibt.

    Kannst Du das eventuell adaptieren, oder eine fertige Library dazu nutzen?

    Rolf

    --
    sumpsi - posui - clusi
    1. Guten Morgen Rolf,

      ich habe es mir nicht genauer angeschaut, aber ich weiß, dass es für die Synchronisierung von Computern in paketvermittelten Netzen wie IP das Protokoll NTP gibt.

      Ja, auf NTP hatte mich dedlfix schon damals aufmerksam gemacht.

      Kannst Du das eventuell adaptieren, oder eine fertige Library dazu nutzen?

      Ich habe ja keinen Zugriff auf die Clients und kann deren Uhr ja nicht synchronisieren.

      Gruß
      Jo

      1. hallo

        Ich habe ja keinen Zugriff auf die Clients und kann deren Uhr ja nicht synchronisieren.

        Du kannst aber die Abhängigkeit von Uhren drastisch reduzieren. Verwende ausschliesslich die Servertime.

        1. Hey,

          Ich habe ja keinen Zugriff auf die Clients und kann deren Uhr ja nicht synchronisieren.

          Du kannst aber die Abhängigkeit von Uhren drastisch reduzieren. Verwende ausschliesslich die Servertime.

          Das mache ich ja gerade indem ich jedem Client alle 10ms die aktuelle Serverzeit schicke. Aber das würde ich gerne vermeiden da ich nicht weiß welche Last dies beim Server erzeugt wenn 'viele' Clients gerade verbunden sind.

          Gruß
          Jo

          1. Tach!

            Du kannst aber die Abhängigkeit von Uhren drastisch reduzieren. Verwende ausschliesslich die Servertime.

            Das mache ich ja gerade indem ich jedem Client alle 10ms die aktuelle Serverzeit schicke. Aber das würde ich gerne vermeiden da ich nicht weiß welche Last dies beim Server erzeugt wenn 'viele' Clients gerade verbunden sind.

            Müssen denn die Clients Quellen der Uhrzeit sein? Ginge es auch, dass clientseitige Ereignisse erst dann einen Timestamp bekommen, wenn die Nachrichten dazu auf dem Server eintreffen?

            dedlfix.

            1. Moin,

              Müssen denn die Clients Quellen der Uhrzeit sein? Ginge es auch, dass clientseitige Ereignisse erst dann einen Timestamp bekommen, wenn die Nachrichten dazu auf dem Server eintreffen?

              Das eigentliche Problem kurz umrissen: ein Benutzer startet irgendeinen Prozess und schickt einen dementsprechenden Request zum Server. Dieser verarbeitet dies und schreibt Start (Serverzeit) und Endzeitpunkt (Serverzeit + Prozesszeit) in eine Datenbank. Danach schickt der Server die neuen Daten wieder zum Client, ob der Prozess gestartet wurde oder ob dies nicht geklappt hat etc. Der Client schaut nun ob es geklappt hat und setzt einen Timer der Runterzählt bis der Endzeitpunkt erreicht ist. Wenn diese Zeit erreicht ist, dann schickt der Client wieder einen Request zum Server um die neusten Daten zu erhalten.

              Wenn nun der Client um 1 Sekunde dem Server nachläuft, dann ist der Prozess zwar für den Client beendet aber nicht für den Server. Was dann zu Problemen führt wie zum Beispiel das der Timer nach dem Response wieder für eine Sekunde aufblitzen oder ähnliches.

              Also Nein, das Event wird Clientseitig durch den Client Timestamp ausgelöst.

              Gruß
              Jo

              1. hallo

                Moin,

                Müssen denn die Clients Quellen der Uhrzeit sein? Ginge es auch, dass clientseitige Ereignisse erst dann einen Timestamp bekommen, wenn die Nachrichten dazu auf dem Server eintreffen?

                Das eigentliche Problem kurz umrissen: ein Benutzer startet irgendeinen Prozess und schickt einen dementsprechenden Request zum Server. Dieser verarbeitet dies und schreibt Start (Serverzeit) und Endzeitpunkt (Serverzeit + Prozesszeit) in eine Datenbank.

                Dein Server verarbeitet also Daten ohne zuvor ein Minimum an Initialisierungen an den Client geliefert zu haben?

                Danach schickt der Server die neuen Daten wieder zum Client, ob der Prozess gestartet wurde oder ob dies nicht geklappt hat etc. Der Client schaut nun ob es geklappt hat und setzt einen Timer der Runterzählt bis der Endzeitpunkt erreicht ist. Wenn diese Zeit erreicht ist, dann schickt der Client wieder einen Request zum Server um die neusten Daten zu erhalten.

                Wenn nun der Client um 1 Sekunde dem Server nachläuft, dann ist der Prozess zwar für den Client beendet aber nicht für den Server. Was dann zu Problemen führt wie zum Beispiel das der Timer nach dem Response wieder für eine Sekunde aufblitzen oder ähnliches.

                Also Nein, das Event wird Clientseitig durch den Client Timestamp ausgelöst.

                Und hier liegt dein Irrtum: Ein Client kann als Referenz immer den Server Timestamp verwenden. (Vorausgesetzt man Initialisiert die Verkehregeln entsprechend). Lediglich für Stoppuhren wird der Client seine eigene Uhr verwenden. Alle Zeitkommunkation aber bezieht sich auf den Server Timestamp.

                Somit dürfte es nicht mal eine Rolle spielen, ob ein Client die Uhrzeit 1970 anzeigt.

                1. Hey,

                  Dein Server verarbeitet also Daten ohne zuvor ein Minimum an Initialisierungen an den Client geliefert zu haben?

                  Was soll der Server denn Initialisieren? Index.html/style.css etc. da bin ich längst drüber hinaus, es geht um einen Button der betätigt wird und dann der Prozess beginnt.

                  Und hier liegt dein Irrtum: Ein Client kann als Referenz immer den Server Timestamp verwenden. (Vorausgesetzt man Initialisiert die Verkehregeln entsprechend).

                  Die Server zeit schicke ich wohl mit dem Response Objekt mit.

                  Lediglich für Stoppuhren wird der Client seine eigene Uhr verwenden. Alle Zeitkommunkation aber bezieht sich auf den Server Timestamp.

                  Ich habe praktisch nur uhren/timer die auf die Endzeit runterzählen.

                  Somit dürfte es nicht mal eine Rolle spielen, ob ein Client die Uhrzeit 1970 anzeigt.

                  Gruß
                  Jo

              2. Tach!

                Das eigentliche Problem kurz umrissen: ein Benutzer startet irgendeinen Prozess und schickt einen dementsprechenden Request zum Server. Dieser verarbeitet dies und schreibt Start (Serverzeit) und Endzeitpunkt (Serverzeit + Prozesszeit) in eine Datenbank. Danach schickt der Server die neuen Daten wieder zum Client, ob der Prozess gestartet wurde oder ob dies nicht geklappt hat etc. Der Client schaut nun ob es geklappt hat und setzt einen Timer der Runterzählt bis der Endzeitpunkt erreicht ist. Wenn diese Zeit erreicht ist, dann schickt der Client wieder einen Request zum Server um die neusten Daten zu erhalten.

                Geht dann nicht, eine bidirektionale Kommunikation einzurichten, bei der der Server die Erledigung meldet, statt dem Poll bei Timeout? Die Techniken dazu sind jedenfalls vorhanden, sei es nackiges Websocket also auch Alternativen wie Long Polling und Forever Frame, sowie Frameworks wie SignalR.

                Wenn nun der Client um 1 Sekunde dem Server nachläuft, dann ist der Prozess zwar für den Client beendet aber nicht für den Server. Was dann zu Problemen führt wie zum Beispiel das der Timer nach dem Response wieder für eine Sekunde aufblitzen oder ähnliches.

                Schickst du dann den Endzeitpunkt als Timestamp statt die Laufzeit als Timespan oder Anzahl der Sekunden?

                Wenn die Laufzeit so exakt feststeht, dass man den Endzeitpunkt angeben kann, kann ich mir laienhaft vorstellen, dass nach Ablauf des Timers auf dem Client er einfach auf 0 stehenbleibt für den Rest der Zeit.

                dedlfix.

                1. Hey,

                  Geht dann nicht, eine bidirektionale Kommunikation einzurichten, bei der der Server die Erledigung meldet, statt dem Poll bei Timeout? Die Techniken dazu sind jedenfalls vorhanden, sei es nackiges Websocket also auch Alternativen wie Long Polling und Forever Frame, sowie Frameworks wie SignalR.

                  Natürlich bidirektional, über einen Websocket schicke ich ja schon jetzt alle 10ms die Serverzeit.

                  Schickst du dann den Endzeitpunkt als Timestamp statt die Laufzeit als Timespan oder Anzahl der Sekunden?

                  Nein den Endzeitpunkt. Wie sollte ich die laufzeit als Zeitspanne schicken?

                  Wenn die Laufzeit so exakt feststeht, dass man den Endzeitpunkt angeben kann, kann ich mir laienhaft vorstellen, dass nach Ablauf des Timers auf dem Client er einfach auf 0 stehenbleibt für den Rest der Zeit.

                  Ich habe ihn so gebaut das er sich selbst entfernt wenn er null erreicht und dann einen Request zum Server schickt. Wenn die Antwort vom Server zurückkommt und die Endzeit für den Server noch in der Zukunft liegt, dann wird ein neuer Timer erstellt der dann für kurze Zeit wieder sichtbar ist und bei Null, einen weiteren Request zum Server schickt

                  Gruß
                  Jo

                  1. Tach!

                    Schickst du dann den Endzeitpunkt als Timestamp statt die Laufzeit als Timespan oder Anzahl der Sekunden?

                    Nein den Endzeitpunkt. Wie sollte ich die laufzeit als Zeitspanne schicken?

                    Timespan = Endzeit - Startzeit; // Zeiten bezogen auf den Server

                    Ist dann kein DateTime mehr sondern eine Number, repräsentierend eine Anzahl von Sekunden (oder Millisekunden).

                    Wenn die Laufzeit so exakt feststeht, dass man den Endzeitpunkt angeben kann, kann ich mir laienhaft vorstellen, dass nach Ablauf des Timers auf dem Client er einfach auf 0 stehenbleibt für den Rest der Zeit.

                    Ich habe ihn so gebaut das er sich selbst entfernt wenn er null erreicht und dann einen Request zum Server schickt. Wenn die Antwort vom Server zurückkommt und die Endzeit für den Server noch in der Zukunft liegt, dann wird ein neuer Timer erstellt der dann für kurze Zeit wieder sichtbar ist und bei Null, einen weiteren Request zum Server schickt

                    Wenn der Client sehr in der Zukunft hängt, dann ist jede Zeit bereits beendet. Ein Timespan hingegen ist relativ und bezieht sich auf die Entfernung zur aktuellen Uhrzeit. Egal welche lokale Zeit ein Client hat, 5 Sekunden sind immer gleich lang (davon ausgehend, dass die Uhren nicht zu unterschiedlich ticken).

                    dedlfix.

                    1. Hey,

                      Timespan = Endzeit - Startzeit; // Zeiten bezogen auf den Server

                      Ist dann kein DateTime mehr sondern eine Number, repräsentierend eine Anzahl von Sekunden (oder Millisekunden).

                      Die Daten liegen dem Client vor und kann auch dort berechnet werden.

                      Aber da bringst du mich gerade auf eine Idee wie ich es nur mit performance.now() lösen kann.

                      Da die Serverzeit mit den Daten geliefert wird ist der Endzeitpunkt:

                      var end = endZeit - serverZeit + performance.now();
                      if(end <= performance.now()){request();}
                      

                      Das müsste funkionieren. Für die Implementierung brauch ich aber sicher 2-3 Tage.

                      Gruß
                      Jo

              3. Das eigentliche Problem kurz umrissen: ein Benutzer startet irgendeinen Prozess und schickt einen dementsprechenden Request zum Server. Dieser verarbeitet dies und schreibt Start (Serverzeit) und Endzeitpunkt (Serverzeit + Prozesszeit) in eine Datenbank. Danach schickt der Server die neuen Daten wieder zum Client, ob der Prozess gestartet wurde oder ob dies nicht geklappt hat etc. Der Client schaut nun ob es geklappt hat und setzt einen Timer der Runterzählt bis der Endzeitpunkt erreicht ist. Wenn diese Zeit erreicht ist, dann schickt der Client wieder einen Request zum Server um die neusten Daten zu erhalten.

                Wenn nun der Client um 1 Sekunde dem Server nachläuft, dann ist der Prozess zwar für den Client beendet aber nicht für den Server. Was dann zu Problemen führt wie zum Beispiel das der Timer nach dem Response wieder für eine Sekunde aufblitzen oder ähnliches.

                Grundsätzlich finde ich die ganze Fragestellung ja schon spannend! Nur ist mir völlig unklar, wie der Server denn überhaupt exakt wissen kann, wann der "Endzeitpunkt" eintritt. Er kann schätzen, raten... aber wissen? Prozesszeit ist nie exakt und vielen Faktoren abhängig…

                m.E. ist daher der Ansatz schon kaputt.

                1. Hey,

                  Grundsätzlich finde ich die ganze Fragestellung ja schon spannend! Nur ist mir völlig unklar, wie der Server denn überhaupt exakt wissen kann, wann der "Endzeitpunkt" eintritt. Er kann schätzen, raten... aber wissen? Prozesszeit ist nie exakt und vielen Faktoren abhängig…

                  Das stimmt natürlich aber es reicht mir schon wenn der Timestamp in der Datenbank mit dem Timestamp der Serverzeit übereinstimmt.

                  So herum könnte man es auch machen, dass der Server schaut, wann ist der Endzeitpunkt erreicht und dann die neuen Daten an den Client schickt. Aber so wollte ich es gerade nicht aufbauen um die Grundlast vom Server gering zu halten und nur auf Anfrage vom Client die Verarbeitung zu starten.

                  m.E. ist daher der Ansatz schon kaputt.

                  Möglich aber durch Fehler lernt man am besten.

                  Gruß
                  Jo

                  1. Das stimmt natürlich aber es reicht mir schon wenn der Timestamp in der Datenbank mit dem Timestamp der Serverzeit übereinstimmt.

                    Wenn das so ist, ist es schön. Scherz, denn:

                    So herum könnte man es auch machen, dass der Server schaut, wann ist der Endzeitpunkt erreicht und dann die neuen Daten an den Client schickt. Aber so wollte ich es gerade nicht aufbauen um die Grundlast vom Server gering zu halten und nur auf Anfrage vom Client die Verarbeitung zu starten.

                    Mir ist noch nicht so ganz klar, was Du eigentlich konkret vorhast. Nur davon abhängig kann man auch eine Einschätzung geben, welche Strategie die passende ist.

                    m.E. ist daher der Ansatz schon kaputt. Möglich aber durch Fehler lernt man am besten.

                    +1

          2. hallo

            Hey,

            Ich habe ja keinen Zugriff auf die Clients und kann deren Uhr ja nicht synchronisieren.

            Du kannst aber die Abhängigkeit von Uhren drastisch reduzieren. Verwende ausschliesslich die Servertime.

            Das mache ich ja gerade indem ich jedem Client alle 10ms die aktuelle Serverzeit schicke. Aber das würde ich gerne vermeiden da ich nicht weiß welche Last dies beim Server erzeugt wenn 'viele' Clients gerade verbunden sind.

            Warum brauchst du das denn? Ein Client muss nur den letzte relevanten Transactions-timstamp kennen, und kann den Rest über Zeitdifferenz notfalls definieren. Es gibt keinen Grund timestamps zu spammen.

            1. Hey,

              Warum brauchst du das denn? Ein Client muss nur den letzte relevanten Transactions-timstamp kennen, und kann den Rest über Zeitdifferenz notfalls definieren. Es gibt keinen Grund timestamps zu spammen.

              Weil das ein einfacher und vorläufiger Workaround war, der mir kurzzeitig etwas Kopfzerbrechen erspart hat, da ich eigentlich etwas anderes machen wollte. Jetzt habe ich Zeit mich intensiver damit zu Beschäftigen und nun wird es richtig gemacht.

              Gruß
              Jo

          3. Das mache ich ja gerade indem ich jedem Client alle 10ms die aktuelle Serverzeit schicke.

            Sind Deine Clients so vergesslich daß sie ihre eigene Uhr nach wenigen Millisekunden nicht mehr kennen?

            Wie auch immer, es gibt Protokolle. Nutze sie anstatt das Rad neu erfinden zu wollen.

            MfG

            1. Hey,

              Sind Deine Clients so vergesslich daß sie ihre eigene Uhr nach wenigen Millisekunden nicht mehr kennen?

              Nein, aber hattest du schon einmal mehrere setTimeout laufen? die Laufen nach einen Zeit auseinander. Außerdem sagt dir das Abtasttheorem sicher etwas. Wenn also eine Funktion 5 mal Pro Sekunde (10Hz) die aktuelle Zeit abfragt, dann sollte diese mindestens alle 10 mal pro Sekunde aktualliesiert werden.

              Wie auch immer, es gibt Protokolle. Nutze sie anstatt das Rad neu erfinden zu wollen.

              Ok, wie führe ich eine Synchronisation auf deinem Rechner aus wenn du einen Request zu meinem Server schickst?

              Gruß
              Jo

              1. Wie gesagt, es gibt Protokolle. Und in denen kommen auch bestimmte Algorithmen zu Einsatz welche den Restfehler, den es im Übrigen immer gibt, auf ein Minimum bringen.

                Natürlich kannst Du der Meinung sein daß Du es besser kannst und einen eigenen ALgorihmus entwickeln. Offensichtlich jedoch suchst Du einen Solchen. Weil Dir aus irgendeinem Grund nicht das reicht, was andere bereits entwickelt haben.

                Einer der Grunde kann aber auch sein, daß Dein Ansatz bzw. Deine Herangehensweise falsch ist.

                1. Natürlich kannst Du der Meinung sein daß Du es besser kannst und einen eigenen ALgorihmus entwickeln. Offensichtlich jedoch suchst Du einen Solchen. Weil Dir aus irgendeinem Grund nicht das reicht, was andere bereits entwickelt haben.

                  Ich glaube nicht das ich besser bin aber ich fand bis eben kein Protokoll oder Modul was dies macht wie ich es möchte/brauche.

                  Einer der Grunde kann aber auch sein, daß Dein Ansatz bzw. Deine Herangehensweise falsch ist.

                  Möglich aber das kann ich nicht beurteilen.

                  Gruß
                  Jo

                  1. Hallo Jo,

                    ich verstehe dein Thema so:

                    Du hast also einen Client-Request. Der startet auf dem Server eine Aktivität. Und diese Aktivität hat eine geschätzte Laufzeit. Der Client soll sich erst nach Ablauf dieser geschätzten Laufzeit wieder melden. Dann gib doch vom Server einfach diese geschätzte Laufzeit zurück. Der Client setzt basierend darauf mit setTimeout(handler, estimatedTime) einen Timeout, nach dessen Ablauf das Ergebnis geholt wird.

                    Welche Latenz hast Du bei Pings zu deinem Server? 100ms? Das wäre schon viel. Dein Client holt also mit maximal 100ms Verspätung die Antwort. Ist das tolerierbar? Oder programmierst Du unter Realtime-Bedingungen (wobei JavaScript dann nicht unbedingt die beste Plattform ist).

                    Angenommen, der Client meldet sich zu früh. Kannst Du am Server ermitteln, wie die vermutete Restlaufzeit ist? Wenn ja, kannst Du dem Client "hab noch xxx Sekunden Geduld" zurückgeben und der setzt sich das dann wieder als Wartezeit.

                    Alles besser als ein Uhrzeitrundfunk - den Du ohne Websockets ohnehin per Longpolling oder ähnlichen Methoden implementieren musst.

                    Achso - Websockets. Wäre das eine Alternative? Der Client wartet GAR NICHT. Er bekommt nur vom Server per Websocket die Antwort zugeschickt, sobald sie fertig ist.

                    Rolf

                    --
                    sumpsi - posui - clusi
                    1. Hey,

                      ich verstehe dein Thema so:

                      Du hast also einen Client-Request. Der startet auf dem Server eine Aktivität. Und diese Aktivität hat eine geschätzte Laufzeit. Der Client soll sich erst nach Ablauf dieser geschätzten Laufzeit wieder melden. Dann gib doch vom Server einfach diese geschätzte Laufzeit zurück. Der Client setzt basierend darauf mit setTimeout(handler, estimatedTime) einen Timeout, nach dessen Ablauf das Ergebnis geholt wird.

                      Naja die Funktion die prüft ob die Endzeit erreicht ist läuft auf dem Client unabhängig davon alle 25ms einmal durch.

                      Welche Latenz hast Du bei Pings zu deinem Server? 100ms? Das wäre schon viel. Dein Client holt also mit maximal 100ms Verspätung die Antwort. Ist das tolerierbar? Oder programmierst Du unter Realtime-Bedingungen (wobei JavaScript dann nicht unbedingt die beste Plattform ist).

                      Ich versuche wenigstens so nah an Realtime heranzukommen wie ich es eben schaffe.

                      Angenommen, der Client meldet sich zu früh. Kannst Du am Server ermitteln, wie die vermutete Restlaufzeit ist? Wenn ja, kannst Du dem Client "hab noch xxx Sekunden Geduld" zurückgeben und der setzt sich das dann wieder als Wartezeit.

                      Ja, die Restzeit am Server ist kein Problem. Darauf könnte es vielleicht hinauslaufen. Also das der Client mitteilt, bei mir ist sekunde x erreicht und der Server wartet mit der Verarbeitung bis Sekunde x auch beim Server erreicht ist.

                      Achso - Websockets. Wäre das eine Alternative? Der Client wartet GAR NICHT. Er bekommt nur vom Server per Websocket die Antwort zugeschickt, sobald sie fertig ist.

                      Läuft alles schon mit einem Websocket mit Node.js und socket.io. Mein Server weiß aber gar nicht wann die Endzeit erreicht ist, der schaut nur in der Datenbank nach und vergleicht mit seiner Zeit. Und darauf hin wird eben entsprechend geändert/bearbeitet.

                      Gruß
                      Jo

                    2. Hey,

                      Welche Latenz hast Du bei Pings zu deinem Server? 100ms? Das wäre schon viel. Dein Client holt also mit maximal 100ms Verspätung die Antwort. Ist das tolerierbar? Oder programmierst Du unter Realtime-Bedingungen (wobei JavaScript dann nicht unbedingt die beste Plattform ist).

                      Latenz gerade getestet und liegt durchschnittlich zwischen 25 und 80 ms für einen Request zum Server.

                      Gruß
                      Jo

                      1. @@J o

                        Latenz gerade getestet

                        Übers Mobilfunknetz?

                        LLAP 🖖

                        --
                        „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
                        1. Hey,

                          Übers Mobilfunknetz?

                          Nein, im Wlan meiner Universität.

                          Gruß
                          Jo

                          1. @@J o

                            Übers Mobilfunknetz?

                            Nein, im Wlan meiner Universität.

                            Soll deine Anwendung nur unter Idealbedingungen laufen oder auch unter Normalbedingungen? Oder soll sie gar robust sein und auch unter schlechten Bedingungen laufen?

                            LLAP 🖖

                            --
                            „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
                            1. @@J o

                              Übers Mobilfunknetz?

                              Nein, im Wlan meiner Universität.

                              Soll deine Anwendung nur unter Idealbedingungen laufen oder auch unter Normalbedingungen? Oder soll sie gar robust sein und auch unter schlechten Bedingungen laufen?

                              Selbstverständlich robust und immer. Aber ich bekomme es ja schon unter guten Bedingungen nicht hin.

                              Gruß
                              Jo

  4. Hi,

    Dazu habe ich damals vorläufig ein kleines Workaround gebastelt, bei dem der Server seine Zeit alle 10ms zu allen Clients schickt. Dies sollte jedoch schon da keine Dauerlösung sein.

    Hm.

    Welche Toleranzen hast Du denn einzuhalten?

    Reicht es nicht, wenn der Client beim einmaligen[1] Erhalt der Serverzeit die Differenz zur Clientzeit berechnet und, wenn er Zeiten an den Server sendet, diese Differenz auf seine Client-Uhrzeit wieder draufrechnet?

    cu,
    Andreas a/k/a MudGuard


    1. von mir aus auch alle n Sekunden wieder neu die Serverzeit senden (oder wenn der Server sowieso Daten an den Client sendet) und die Differenz neu berechnen - wenn wirklich die Gefahr besteht, daß die Uhren auf Client und Server eine die Toleranz überschreitende Abweichung erzeugen ↩︎

    1. Guten Morgen,

      Welche Toleranzen hast Du denn einzuhalten?

      Also ich brauche eigentlich nur die selbe Sekunde.

      Reicht es nicht, wenn der Client beim einmaligen Erhalt der Serverzeit die Differenz zur Clientzeit berechnet und, wenn er Zeiten an den Server sendet, diese Differenz auf seine Client-Uhrzeit wieder draufrechnet?

      [...] von mir aus auch alle n Sekunden wieder neu die Serverzeit senden (oder wenn der Server sowieso Daten an den Client sendet) und die Differenz neu berechnen - wenn wirklich die Gefahr besteht, daß die Uhren auf Client und Server eine die Toleranz überschreitende Abweichung erzeugen

      Ja das hatte ich heute Morgen auch schon überlegt. Ich werde mich nachher mal dran setzen und ein kleines Objekt kreieren welches in Abständen die Latenz überprüft und die Differenz angleicht, mal schauen ob das funktioniert.

      Dies funktioniert schon ganz gut, doch da hab ich immer noch eine konstante Differenz von 500ms. Und wenn dies gerade zwischen x.501 und x.999 liegt hat einer der beiden gerade die nächste
      Sekunde (x + 1).

      Gruß
      Jo

      1. Hallo Jo,

        eigentlich müsstest Du doch mit dem SNTP-Algorithmus klarkommen können. Ich habe ihn mal um einen gleitenden Durchschnitt der Zeitdifferenzen angereichert...

        class SyncedTime {
           constructor() {
              this.delta = 0;
           }
           now() { 
              return Date.now() + this.delta;
           }
           static synchronize(timeService, syncDelay) {
              let deltaList = [];
              let avgLength = 3;
              let tickCount = 0;
              let syncObject = new SyncedTime();
              
              return new Promise(doSync);
        
              function doSync(resolve, reject) {
                 let syncStart = Date.now();
                 timeService()
                 .then(servertime => {
                    // Client-/Server Delta für diesen Aufruf berechnen.
                    let syncEnd = Date.now();
                    let ipDelay = (syncEnd - syncStart) / 2;
                    changeDelta(servertime + ipDelay - syncEnd);
        
                    // Nächsten Zyklus einleiten - in der Init-Phase mehrmals schnell hintereinander un
                    // Initialisierungseffekte zu eliminieren.
                    if (tickCount < avgLength) {
                       setTimeout(() => doSync(resolve,reject), 0);
                       tickCount++;
                    }
                    else {
                       // Nur einmal resolven. Alle Folgeaufrufe mit resolve/reject=null durchführen.
                       if (resolve) resolve(syncObject);
                       setTimeout(() => doSync(null,null), syncDelay);
                    }
                 });
              }
              function changeDelta(delta) {
                 console.log("Apply new delta " + delta);
                 // Gleitenden Durchschnitt der letzten 3 Deltas berechnen
                 deltaList.push(delta);
                 if (deltaList.length > avgLength) deltaList.shift();
                 syncObject.delta = deltaList.reduce((x,y)=>x+y, 0)/deltaList.length;
              }
           }
        } 
        

        Aufzurufen mit

        SyncedTime
           .synchronize(timeService, 10000)
           .then(myProgramWithServerTime);
        

        timeService ist eine Funktion, die die Serverzeit holt und ein Promise liefert, das zur zurückgelieferten Serverzeit resolved. Wenn Du das nicht hast oder haben willst, bau es so um dass die Funktion einen Callback erwartet, dem die Serverzeit übergeben wird. Läuft auf's selbe hinaus, aber Promises sind schicker :)

        Auf diese Weise geht es im then-Handler weiter, sobald syncTime eingeschwungen ist (d.h. einmal mehr synchronisiert hat als die Länge des gleitenden Durchschnitts), und er bekommt ein SyncedTime Objekt übergeben, das sich automatisch mit dem Server synchronisiert und über die now-Methode die synchronisierte Zeit liefert.

        Die SyncedTime Klasse muss gar nicht so oft synchronisieren, man wird davon ausgehen können dass Client- und Server-Uhr in bspw. 5 Minuten nur um wenige Mikrosekunden auseinander laufen. Die 10 Sekunden aus meinem Aufrufbeispiel sind vermutlich viel zu viel.

        Damit bekommst Du dann eine relativ genaue Annäherung der Clientzeit an die Serverzeit hin. Eine (hypothetische) Funktion zf(t), die zum Zeitpunkt t den aktuellen Zeitfehler angibt, dürfte im Betrag unter 100ms bleiben, vermutlich weniger.

        Wichtig ist nur, dass der Server die Time-Requests nicht queued, sondern sofort verarbeitet. Bei einem stark belasteten Server kann es zum Queueing kommen, wenn zu viele Requeste eintreffen. Die Laufzeitbalance von Request und Response ist dann schief. Es kann sinnvoll sein, für die Time Requests einen separaten Node laufen zu lassen.

        Das Problem beginnt, wenn Du anfängst, auf Sekunden zu runden. Aus dem stets nahe bei 0 liegenden Fehlerwert wird dann auf einmal eine Rechteck-Kurve, die meistens auf 0 liegt, aber gelegentlich auf 1 oder -1 springt. Das Integral von zf und gerundetem zf dürfte über die Zeit gleich sein, aber wenn dich die heftigen Ausschläge stören, müsste man fragen: Musst Du runden?

        Rolf

        --
        sumpsi - posui - clusi
        1. Hallo Rolf,

          Erstmal vielen Dank für deine Mühe.

          Die SyncedTime Klasse muss gar nicht so oft synchronisieren, man wird davon ausgehen können dass Client- und Server-Uhr in bspw. 5 Minuten nur um wenige Mikrosekunden auseinander laufen. Die 10 Sekunden aus meinem Aufrufbeispiel sind vermutlich viel zu viel.

          Das denke ich auch, mal schauen welches Intervall ich dann wählen werde.

          Wichtig ist nur, dass der Server die Time-Requests nicht queued, sondern sofort verarbeitet. Bei einem stark belasteten Server kann es zum Queueing kommen, wenn zu viele Requeste eintreffen. Die Laufzeitbalance von Request und Response ist dann schief. Es kann sinnvoll sein, für die Time Requests einen separaten Node laufen zu lassen.

          Ich vermute, das habe ich schon unbedacht gelöst aber da möchte ich mich noch nicht zu weit aus dem Fenster lehnen. Hier einmal mein Ansatz:

          var _sync = function () {
          	this.total = {};
          	this.checkList = [];
          	this.checkLength = 5;
          	this.check = function () {
          		for ( let i = 1; i <= 5; i++ ) {
          			// webSocket ist ein Objekt welches alle Requests an den Server beinhaltet.
          			// webSocket.latency schickt das erzeugte Object zum Server
          			// dort wird die Serverzeit ergänzt und das Objekt wird wieder zum Client geschickt
          			// danach wird _sync.set aufgerufen und das Objekt in die checkList gepusht
          			setTimeout(function(){ webSocket.latency( { clientStart: performance.now(), client: new Date().getTime() } ) }, 250 * i );
          		}
          	}
          	this.set = function ( res ) {
          		this.checkList.push( res );
          		// wenn die checkList voll ist wird der Mittelwert aus allen Zeiten berechnet
          		// und in this.total gespeichert
          		if ( this.checkList.length === this.checkLength ) {
          			var latency = 0;
          			var total = {clientStart: 0, server: 0, clientEnd: 0, diff: 0, latency: 0};
          			for ( v of this.checkList ) {
          				v.diff = v.client - v.server;
          				v.latency = (v.clientEnd - v.clientStart) / 2;
          				for ( k in total ) {
          					total[k] += v[k];
          				}
          			}
          			for ( k in total ) {
          				total[k] /= this.checkLength;
          			}
          			this.total = Object.assign( {}, total );
          			this.checkList = [];
          		}
          	}
          	this.time = function () {
          		// hier kann dann die Serverzeit aus this.total und performance.now() berechnet werden
          		return new Date(this.total.server + Math.floor(performance.now()) - this.total.clientStart).toLocaleTimeString();
          	}
          }
          

          Getestet mit:

          setTimeout(function(){ setInterval(function(){ console.log(_sync.time()); webSocket.test() }, 5000); }, 6000 );
          

          Wobei webSocket.test() nur den Server anpingt und dieser dann seine Zeit in der Konsole ausgibt.

          Und siehe da die Abweichung entspricht genau der Latenz! [ Der Server loggt Serverzeit + Latenz) auf 10 Millisekunden genau ].

          Ich muss mir nochmal genau anschauen wie ich eine Grundlast des Servers erzeugen kann, da gibt es ein Modul für Node.js, meine ich in Erinnerung zu haben. Dann kann ich auch sehen welche Ergebnisse diese Berechnung für queued requests liefert.

          Gruß
          Jo

          1. Ich muss mir nochmal genau anschauen wie ich eine Grundlast des Servers erzeugen kann, da gibt es ein Modul für Node.js, meine ich in Erinnerung zu haben. Dann kann ich auch sehen welche Ergebnisse diese Berechnung für queued requests liefert.

            cpuburn 😉

            1. Hey,

              cpuburn 😉

              Naja, nicht voll Auslasten, dann geht ja garnichts mehr.?
              Artillery.io wars es, ich wusste es war was mit Kanonen!

              Gruß
              Jo