我发现上面@mehdi-yeganeh 的算法没有给我有用的结果,但这个想法是合理的:使用 NTP 算法(或至少是它的弱版本)来同步服务器和客户端时钟。
这是我的最终实现,它使用服务器响应标头以提高准确性(如果我错了,请纠正我,我自己的测试表明这是非常准确的)。
浏览器端(javascript):
// 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)));
}
});
服务器端(php,但可以是任何东西):
您在路由 'GET /ntp' 上的服务器应返回如下内容:
echo (string) round(microtime(true) * 1000);
如果您的 PHP > 5.4,那么您可以保存对 microtime() 的调用,并通过以下方式使其更准确:
echo (string) round($_SERVER['REQUEST_TIME_FLOAT'] * 1000);
笔记
这种方式可能被视为一种贫民窟,还有一些其他 Stack Overflow 答案可以指导您找到更好的解决方案: