Podczas pisania aplikacji konkursowej na konferencję WROC# pojawił się ciekawy problem, który wydał mi się warty opisania. O danej godzinie, podczas przerwy, administrator aplikacji uruchamiał test. Następnie, podczas 10 minut, każda z zarejestrowanych osób mogła odpowiedzieć na pytanie testowe i wziąć udział w konkursie.
Jedną z funkcjonalności tej aplikacji był licznik czasu przy pytaniu testowym, który odmierzał czas do zakończenia konkursu. Gdy czas doszedł do zera, strona przekierowywała uczestnika konkursu na stronę z informacją o zakończeniu testu. Dana funkcjonalność działała świetnie na komputerze lokalnym, ale po uruchomieniu aplikacji na chmurze Azure okazało się, że użytkownik jest około 2 minuty do przodu w stosunku do czasu na serwerze. Powodowało to problemy z zakończeniem testu - strona przekierowywała użytkownika na podsumowanie, które jeszcze w tym momencie nie było dostępne.
Ratunkiem okazał się poniższy kod:
(function () {
var timeSync = {};
window.timeSync = timeSync;
timeSync.url = "/Home/GetServerTime";
timeSync.httpMethod = "GET";
timeSync.dataType = "json";
timeSync.contentType = "application/json; charset=utf-8";
timeSync.getTimeDifference = function () {
var roundTripStart = new Date();
var clientTime = new Date();
return $.ajax({
type: timeSync.httpMethod,
url: timeSync.url,
dataType: timeSync.dataType,
contentType: timeSync.contentType,
async: false
})
.then(function (serverTimeString) {
var roundTrip = new Date().getTime() - roundTripStart.getTime();
var serverTime = new Date(serverTimeString);
var timeDifference = (serverTime.getTime() - roundTrip) - clientTime.getTime();
return timeDifference;
});
}
})()
Jest to funkcja, która pozwala pobrać aktualny czas z serwera i wyliczyć różnicę pomiędzy przeglądarką a zdalnym serwisem. Wzorowałem się na poście, który jednak w rzeczywistym zastosowaniu nie działał poprawnie, a dodatkowo potrzebował kolejnych bibliotek do działania. Tutaj mamy prosty fragment, który robi dokładnie to co potrzebujemy - pobiera czas z serwera, odejmuje czas potrzebny na obsługę żądania i zwraca różnicę pomiędzy czasem serwerowym, a przeglądarką. Do poprawnego działania będziemy potrzebować jeszcze kontrolera HomeController, który będzie zwracał nam odpowiednio sformatowaną godzinę.
public class HomeController : Controller
{
public ActionResult GetServerTime()
{
return Json(DateTime.Now.ToString("yyyy'-'MM'-'ddTHH':'mm':'ss.fff%K"), JsonRequestBehavior.AllowGet);
}
}
Samo wywołanie jest bardzo proste - wywołujemy metodę getTimeDifference i w momencie odczytania różnicy czasów używamy jej do zsynchronizowania naszych dat wyświetlanych użytkownikowi. W aplikacji WROC# użyłem tej metody by poprawnie obsłużyć zakończenie czasu testu.
window.timeSync
.getTimeDifference()
.done(function (timeDifference) {
var endDateString = '@Model.EndDate.ToString("o")';
var endDate = new Date(endDateString);
var clientEndDateTicks = endDate - timeDifference;
var clientEndDate = new Date(clientEndDateTicks);
// użycie liczniak jQuery który odmierza czas i przekierowuje na stronę podsumowania gdy czas się skończy
});
Jeśli ktoś był na WROC# to wie, że synchronizacja działała poprawnie, więc z pełnym zaufaniem mogę wam polecić ten kod :)
Comments:
dotnetomaniak.pl - Apr 1, 2016
Synchronizacja czasu pomiędzy serwerem i przeglądarką | RadBlog
Dziękujemy za dodanie artykułu - Trackback z dotnetomaniak.pl