[英]The best way to synchronize client-side javascript clock with server date
I have a task to show digital clock (with minutes precision) on HTML page in some fixed timezone (MSK or MSD - depending on current date).我有一项任务是在某个固定时区(MSK 或 MSD - 取决于当前日期)的 HTML 页面上显示数字时钟(分钟精度)。 I'd like to avoid relying on client system clock, so some synchronization with server is required.我想避免依赖客户端系统时钟,因此需要与服务器进行一些同步。 HTTP server sends Date header in each response so we can send an AJAX GET or HEAD request to any URL of our site to get server date, calculate the difference with client date and use it when updating clock with setTimeout(). HTTP 服务器在每个响应中发送 Date 标头,因此我们可以向我们站点的任何 URL 发送 AJAX GET 或 HEAD 请求以获取服务器日期,计算与客户端日期的差异并在使用 setTimeout() 更新时钟时使用它。 There are other issues remains: timezone switching for daylight settings, latency accounting for very slow connections.还有其他问题仍然存在:日光设置的时区切换,连接速度非常慢的延迟。
Any idea to this task the simpliest way?对这个任务有最简单的想法吗? I'd prefer to solve it without server-side programming.我更愿意在没有服务器端编程的情况下解决它。
You can calculate exact time with NTP (Network Time Protocol) in your codes,您可以在代码中使用NTP(网络时间协议)计算准确时间,
i try to explain for you:我试着为你解释:
X(SyncedTime) = Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime)
X(SyncedTime) = Now + (ServerClientDifferenceTimeWithRequestTime - RquestTime)
X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)
X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)
Now - ClientTime = RquestTime + 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)
X(SyncedTime) = Now + (ServerClientDifferenceTimeWithResponseTime - ResponseTime)
I show simple code but when you want write it don`t forgot use UTC date & time functions...我展示了简单的代码,但是当你想写它时不要忘记使用 UTC 日期和时间函数......
Server Side (for example php, c#):服务器端(例如 php、c#):
PHP: PHP:
header('Content-Type: application/json; charset=utf-8');
$clientTime = $_GET["ct"] * 1; //for php 5.2.1 or up: (float)$_GET["ct"];
$serverTimestamp = round(microtime(true)*1000); // (new DateTime())->getTimestamp();
$serverClientRequestDiffTime = $serverTimestamp - $clientTime;
echo "{\"diff\":$serverClientRequestDiffTime,\"serverTimestamp\":$serverTimestamp}";
C#: C#:
long clientTime = long.Parse(Request.Form["ct"]);
long serverTimestamp = (DateTime.Now.Ticks-(new DateTime(1970,1,1) - DateTime.MinValue).Ticks) / 10000;
long serverClientRequestDiffTime = serverTimestamp - clientTime;
Response.Write("{\"diff\":"+serverClientRequestDiffTime+",\"serverTimestamp\":"+serverTimestamp+"}");
Client Side (Javascript with Jquery ):客户端(带有Jquery 的Javascript):
var clientTimestamp = (new Date()).valueOf();
$.getJSON('http://yourhost.com/getdatetimejson/?ct='+clientTimestamp, function( data ) {
var nowTimeStamp = (new Date()).valueOf();
var serverClientRequestDiffTime = data.diff;
var serverTimestamp = data.serverTimestamp;
var serverClientResponseDiffTime = nowTimeStamp - serverTimestamp;
var responseTime = (serverClientRequestDiffTime - nowTimeStamp + clientTimestamp - serverClientResponseDiffTime )/2
var syncedServerTime = new Date((new Date()).valueOf() + (serverClientResponseDiffTime - responseTime));
alert(syncedServerTime);
});
These two Javascript functions should do the trick for you.这两个 Javascript 函数应该可以为您解决问题。
var offset = 0;
function calcOffset() {
var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
xmlhttp.open("GET", "http://stackoverflow.com/", false);
xmlhttp.send();
var dateStr = xmlhttp.getResponseHeader('Date');
var serverTimeMillisGMT = Date.parse(new Date(Date.parse(dateStr)).toUTCString());
var localMillisUTC = Date.parse(new Date().toUTCString());
offset = serverTimeMillisGMT - localMillisUTC;
}
function getServerTime() {
var date = new Date();
date.setTime(date.getTime() + offset);
return date;
}
EDIT: removed ".replace(/^(. )[\\s\\S] /,"$1")".编辑:删除了“.replace(/^(. )[\\s\\S] /,”$1”)”。
calcOffset() calculates the offset from server time and compensates for GMT/UTC. calcOffset() 计算与服务器时间的偏移量并补偿 GMT/UTC。
getServerTime() to get the local time offset to match the servers, using the local timezone. getServerTime() 使用本地时区获取本地时间偏移量以匹配服务器。
If calcOffset() takes along time to execute you might loose some seconds precision.如果 calcOffset() 需要很长时间来执行,您可能会失去几秒钟的精度。 Maybe the execution time could be taken into account....也许可以考虑执行时间......
If you are worried about the calculated offset becoming wrong when either local time or server time change to or from daylight savings time you could recalculate a litle after every clock-hour, the system will compensate for changes in dayligt savings time.如果您担心本地时间或服务器时间更改为夏令时或从夏令时更改时计算的偏移量会出错,您可以在每个时钟小时后重新计算一点,系统将补偿夏令时的变化。 It might be necessary to wait until both the local and server clock has passed the hour.可能需要等到本地时钟和服务器时钟都过去了一个小时。
The example only works in IE because of "Msxml2.XMLHTTP" i think.....该示例仅适用于 IE,因为我认为“Msxml2.XMLHTTP”.....
I've found that the algorithm of @mehdi-yeganeh above didn't give me useful results but the idea is sound: to use the NTP algorithm (or at least a weak version of it) to synchronize the server and client clocks.我发现上面@mehdi-yeganeh 的算法没有给我有用的结果,但这个想法是合理的:使用 NTP 算法(或至少是它的弱版本)来同步服务器和客户端时钟。
This is my final implementation, it uses the server response headers if available for extra accuracy (please correct me if I'm wrong, my own tests say this is quite accurate).这是我的最终实现,它使用服务器响应标头(如果可用)以提高准确性(如果我错了,请纠正我,我自己的测试表明这是非常准确的)。
// the NTP algorithm
// t0 is the client's timestamp of the request packet transmission,
// t1 is the server's timestamp of the request packet reception,
// t2 is the server's timestamp of the response packet transmission and
// t3 is the client's timestamp of the response packet reception.
function ntp(t0, t1, t2, t3) {
return {
roundtripdelay: (t3 - t0) - (t2 - t1),
offset: ((t1 - t0) + (t2 - t3)) / 2
};
}
// calculate the difference in seconds between the client and server clocks, use
// the NTP algorithm, see: http://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm
var t0 = (new Date()).valueOf();
$.ajax({
url: '/ntp',
success: function(servertime, text, resp) {
// NOTE: t2 isn't entirely accurate because we're assuming that the server spends 0ms on processing.
// (t1 isn't accurate either, as there's bound to have been some processing before that, but we can't avoid that)
var t1 = servertime,
t2 = servertime,
t3 = (new Date()).valueOf();
// we can get a more accurate version of t2 if the server's response
// contains a Date header, which it generally will.
// EDIT: as @Ariel rightly notes, the HTTP Date header only has
// second resolution, thus using it will actually make the calculated
// result worse. For higher accuracy, one would thus have to
// return an extra header with a higher-resolution time. This
// could be done with nginx for example:
// http://nginx.org/en/docs/http/ngx_http_core_module.html
// var date = resp.getResponseHeader("Date");
// if (date) {
// t2 = (new Date(date)).valueOf();
// }
var c = ntp(t0, t1, t2, t3);
// log the calculated value rtt and time driff so we can manually verify if they make sense
console.log("NTP delay:", c.roundtripdelay, "NTP offset:", c.offset, "corrected: ", (new Date(t3 + c.offset)));
}
});
Your server at route 'GET /ntp' should return something like:您在路由 'GET /ntp' 上的服务器应该返回如下内容:
echo (string) round(microtime(true) * 1000);
If you have PHP >5.4, then you can save a call to microtime() and make it a bit more accurate with:如果您的 PHP > 5.4,那么您可以保存对 microtime() 的调用并使其更准确:
echo (string) round($_SERVER['REQUEST_TIME_FLOAT'] * 1000);
This way might be seen as kind of ghetto, there are some other Stack Overflow answers that could guide you towards a better solution:这种方式可能被视为一种贫民窟,还有一些其他 Stack Overflow 答案可以指导您找到更好的解决方案:
如果您打算使用 ajax,您应该记住 readyState==2 和 readyState==3 之间的客户端时间,因为服务器时间将设置在收到请求和准备响应之间的某个时间
I'd only request the update from the server every 30s or so, if you require precision only to the minute.如果您只需要精确到分钟,我只会每 30 秒左右从服务器请求更新一次。 Don't rely on the client time at all, but use their system clock to keep the clock accurate between updates.不要完全依赖客户端时间,而是使用他们的系统时钟来保持更新之间的时钟准确。 I think you answered your own question?我想你回答了你自己的问题?
It would help if we better understood what you're actually trying to do.如果我们更好地理解您实际尝试做什么,将会有所帮助。
If you simply want a clock to display the time on the server, which is then adjusted to a certain timezone, do it clientside with offsets.如果您只是想要一个时钟在服务器上显示时间,然后将其调整到某个时区,请在客户端使用偏移量。 Handle DST in the timezones it is applicable by using the date you receive from the server as well.通过使用您从服务器收到的日期,在适用的时区处理 DST。 If you want to determine latency, you would probably need a small script on the server to calculated the difference.如果您想确定延迟,您可能需要服务器上的一个小脚本来计算差异。 But as above, it would help to understand the problem better.但如上所述,这将有助于更好地理解问题。 If precision is only to the minute, latency seems less critical.如果精确到分钟,延迟似乎就不那么重要了。
Thanks to @Mehdi Yeganeh and @Fedearne.感谢@Mehdi Yeganeh 和@Fedearne。 I implement my function to use both logic and it's work.我实现了我的功能以使用这两种逻辑并且它的工作原理。
https://gist.github.com/ethaizone/6abb1d437dbe406fbed6 https://gist.github.com/ethaizone/6abb1d437dbe406fbed6
Little too late but hope this might help someone!太晚了,但希望这可以帮助某人!
I had a similar requirement to display a server clock irrespective of client's machine.无论客户端的机器如何,我都有类似的要求来显示服务器时钟。 So basically, you just play with three parameters here:所以基本上,你只需在这里使用三个参数:
x = clientReqTimestamp = (new Date()).valueOf(); //Client Timestamp at request.
y = serverTimestamp; //query your server for Unix Timestamp.
z = clientRespTimestamp = (new Date()).valueOf(); //Client Timestamp on receiving response.
Then do the below calculation:然后进行以下计算:
var reqElapsed = Math.abs(y - x); //time taken in milliseconds to hit the server
var respElapsed = Math.abs(z - y); //time taken in milliseconds to get response from server
var serverNewTime = z + respElapsed; // Voila! actual server time.
Below is the full code in action:下面是运行中的完整代码:
<script>
var clientTimestamp = (new Date()).valueOf();
var Data = {
OperatorMobileNo: 'XXXXXXXXXX',
requestClientTime: clientTimestamp
};
$.ajax({
type: "POST",
url: serviceURLx + "/XXXX/GetServerDateTime/1.0",
dataType: "JSON",
data: JSON.stringify(Data),
contentType: "application/json; charset=utf-8",
success: function (responseData) {
debugger;
var responseJSON = JSON.parse(JSON.stringify(responseData));
if (responseJSON.ResponseCode === "000") {
var x = clientReqTimestamp = clientTimestamp;
// If server time is in seconds => multiply by 1000 to convert sec to milli
var y = serverTimestamp = responseJSON.Response.ServTimestamp * 1000;
var z = clientRespTimestamp = (new Date()).valueOf();
var reqElapsed = Math.abs(y - x);
var respElapsed = Math.abs(z - y);
var serverNewTime = z + respElapsed;
debugger;
//Init Server Clock
setInterval( function() {
debugger;
var servClockT = new Date(serverNewTime += 1000);
document.getElementById('serverClock').innerHTML = servClockT;
}, 1000);
}
else {
swal("", "Unable To Fetch Server Time!", "info");
console.log(responseJSON.ResponseCode);
}
},
error: function () {
}
});
</script>
If I understand question correctly we have only 3 values如果我正确理解问题,我们只有 3 个值
if assume request and response time are equal we can calculate timeDifference between server and client by:如果假设请求和响应时间相等,我们可以通过以下方式计算服务器和客户端之间的 timeDifference:
const diff = serverTime - clientTimeWhenRequestSent
- (clientTimeWhenResponseReceived - clientTimeWhenRequestSent)/2;
and get correct time on client with help of并在客户的帮助下获得正确的时间
const correctClienTime = (new Date()).valueOf() + diff;
I would sync the time during initialization with Internet Time Server.我会在初始化期间与 Internet 时间服务器同步时间。
http://tf.nist.gov/service/its.htm http://tf.nist.gov/service/its.htm
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.