Synchronizacja czasu w aplikacjach - różnice na różnych maszynach w Unix Epoch

0

Cześć,
po raz kolejny próbuje napisać aplikację do grania w szachy przez internet. Porzuciłem pisanie tego w Delphi i zacząłem uczyć się pythona (backend) i javascript (front).

Idzie całkiem nieźle ale trafiłem na przeszkodę związaną z synchronizacją czasu między klientem i serwerem.

Na początku synchronizacja czasu polegała na tym, że:

  1. Serwer odpala czas i wysyła informację do klienta,
  2. Klient aktualizuje swój czas na ten serwera,
  3. Wykonuje ruch i odsyła informacje do serwera,
  4. Serwer wykonuje swoje obliczenia, zatrzymuje czas i odsyła informacje o czasie do klienta.
  5. Klient aktualizuje swój czas na ten serwera.

Niby wszystko gra ale czytałem gdzieś o lagach związanych z samym faktem przesyłania informacji. Zacząłem więc mierzyć opóźnienia w ten sposób:

  1. Klient pobiera u siebie timestamp
  2. Serwer otrzymuje info, pobiera u siebie timestamp, oblicza czas żądania odejmujac od siebie wartosci i to odsyla
  3. Klient znowu pobiera timestamp, i odejmuje od tego aktualnego wartość z serwera. Mamy czas odpowiedzi.

Problem pojawił się u mnie w ostatnim punkcie. Wyszło mi, że najbardziej aktualny timestamp klienta (Now) jest mniejszy od tego z serwera. Zakładałem, że każdy kolejny będzie większy.

Wyniki:

Client stamp: 1586885559.581
Server stamp: 1586885560.620
Now: 1586885559.738

Req diff: 1.039147138595581
Res diff: -0.8821473121643066

Wychodzi przez to, że czas odpowiedzi jest ujemny :P Czy jest możliwość, że czas pobierany na różnych maszynach może się w ten sposób różnić? Czy popełniam jednak jakiś błąd?
Na froncie pobieram to przez:

t = new Date();
t.getTime();

a na backendzie:

from datetime import time
time.time()

Rozwiązałem tą sytuację tworząc api, które zwraca aktualny czas. Teraz i klient i serwer strzalają po ten czas. Wyniki są teraz zgodne z oczekiwanymi.
Czy wyniki, które uzyskałem wcześniej są normalne?

3

W jeden wspólny czas ludzkość ostatni raz wierzyła w czasach Newtona ;)
*(powiedziałem to żartobliwie, ale to trzeba przetrawić we łbie, jest wiele ciekawych poziomów głębiej, fizyki, astronomii, informatyki, telekomunikacji analogowej i cyfrowej)
*

O, jest, znalazłem
To trzeba raz w życiu wysłuchać.

2

oblicza czas żądania odejmujac od siebie wartosci

Skąd wiesz, że obydwie maszyny generują timestamp w taki sam sposób?
Skąd wiesz, że timestamp jest monotoniczny?

Poczytaj sobie o tym, jak działa synchronizacja czasu z wykorzystaniem ntp :-)

0

@Patryk27: Czy dobrze rozumiem, że pobieranie timestampa z jednego źródła przez wszystkie aplikacje chociaż w części rozwiązuje problem monotoniczności i jednakowego generowania? :) Pomijając już kwestie strat czasu przy samym pobieraniu timestampa.

3

Tak, o ile masz dostęp do monotonicznego zegara - a te wcale nie są wszechobecne; przykładowo unix timestamp monotoniczny nie jest (zmiana daty na komputerze powoduje zmianę zwracanego timestampu) ;-)

Np. biblioteka standardowa Rusta zawiera typ Instant, który najwyżej stara się implementować monotoniczny zegar.

0

Zmiana daty masz na myśli kolejny dzień czy ręczne przestawienie daty?

Bo jeżeli mówimy o ręcznych zmianach to dla mnie problemu nie ma bo potrzebuje po prostu takiej samej reprezentacji czasu dla wszystkich aplikacji.

Jeżeli jednak zmiana dnia po godzinie 00:00 powoduje jakieś większe zmiany w timestampie to np dla gracza grającego wtedy partię szachów może być problematyczne - i dla administratora takiego systemu ;)

1

Mam na myśli np. czas letni / zimowy, gdzie nagle masz albo dziurę w numeracji (przeskok o godzinę wprzód), albo cofasz się w czasie :-)

1

Bardzo Wam dziękuję za odpowiedzi. Muszę teraz ogarnąć obliczanie zsynchronizowanego czasu po stronie klienta po requescie i response kiedy mamy już wszystkie wartości. Widziałem kiedy googlowałem, że ilu programistów to tyle wzorów na to ale wydaje się to prosta matematyka więc może dam rade :D

Przykład:

You should Calculate simple equations in client:

X(SyncedTime) = Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime)

X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

Now - ClientTime = RquestTime + ResponseTime =>

Now - (ServerClientDiffRq - RquestTime) = Now - (ServerClientDiffRs - ResponseTime)

if you solve it you found this:

ResponseTime = (ServerClientDifferenceTimeWithRequestTime - Now + ClientTime + - ServerClientDifferenceTimeWithResponseTime )/2

and then you can found synced time or server time in client with this equation:

X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)

1

Właśnie doszedłem do wniosku, że przecież ja nie musze robic tych wszystkich obliczeń powyżej (poza tym coś z nimi jest chyba nie tak).
Dla mnie najważniejsze jest opóźnienie w prekazywaniu informacji i na tej podstawie muszę dostosowywać czas gry gracza i potem już przekazywać ten czas całej reszcie.
Więc ja muszę skupić się na:
Req diff: 0.06
Res diff: 0.08
Lag: 0.14
i odpowiednim operowaniu tymi wartościami na czasie gry obu graczy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1