javascript 在多个设备之间同步JS时间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10585910/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sync JS time between multiple devices
提问by Josh Hunt
I'm using the wonderful reveal.jslibrary to create a HTML slideshow. My only problem is that I need it to synchronise across multiple devices.
我正在使用精彩的reveal.js库来创建HTML 幻灯片。我唯一的问题是我需要它在多个设备之间同步。
At the moment I am making a AJAX request to the the time from the server and keep an internal clock for the page.
目前,我正在向来自服务器的时间发出 AJAX 请求,并为页面保留一个内部时钟。
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
r.open('HEAD', document.location, false);
r.send(null);
var timestring = r.getResponseHeader("DATE");
systemtime = new Date(timestring); // Set the time to the date sent from the server
}
Whilst this gets me within 1 or so seconds of accuracy, I need to do better. The difference is really noticeable when the slideshow is auto advancing.
虽然这让我在 1 秒左右的准确度内得到了回报,但我需要做得更好。当幻灯片自动前进时,差异非常明显。
The code is going to be running all on the same platform, so cross-browser compatibility is nothing to worry about.
代码将全部运行在同一平台上,因此无需担心跨浏览器兼容性。
Here's what I've managed to put together
Any ideas?
有任何想法吗?
采纳答案by josh3736
How about a different approach: who cares about time? (You're not going to reliably sync the system clock with JavaScript.)
换一种不同的方法怎么样:谁在乎时间?(您不会使用 JavaScript 可靠地同步系统时钟。)
Instead, use a Nodeserver with socket.ioto synchronize when your clients advance the slideshow. Instead of the clients deciding when to advance, the server tells them to.
相反,当您的客户端推进幻灯片放映时,使用带有socket.io的节点服务器进行同步。不是客户端决定何时前进,而是服务器告诉他们前进。
This approach comes with the added bonus of being able to manually fiddle with the slideshow while it's running. In the example that follows, I've added a Nextbutton that causes all connected clients to immediately advance to the next slide.
这种方法带来了额外的好处,即能够在幻灯片放映时手动摆弄它。在下面的示例中,我添加了一个Next按钮,该按钮使所有连接的客户端立即前进到下一张幻灯片。
app.js
应用程序.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app)
, doT = require('dot')
, slide = 0
, slides = [
'http://placekitten.com/700/400?image=13',
'http://placekitten.com/700/400?image=14',
'http://placekitten.com/700/400?image=15',
'http://placekitten.com/700/400?image=16',
'http://placekitten.com/700/400?image=1',
'http://placekitten.com/700/400?image=2',
'http://placekitten.com/700/400?image=3',
'http://placekitten.com/700/400?image=4',
'http://placekitten.com/700/400?image=5',
'http://placekitten.com/700/400?image=6',
'http://placekitten.com/700/400?image=7',
'http://placekitten.com/700/400?image=8',
'http://placekitten.com/700/400?image=9',
'http://placekitten.com/700/400?image=10',
'http://placekitten.com/700/400?image=11',
'http://placekitten.com/700/400?image=12',
];
app.listen(70); // listen on port 70
app.register('.html', doT); // use doT to render templates
app.set('view options', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don't strip line endings from template file
app.get('/', function(req, res) {
res.render('index.html', { slide: slide, slides: slides });
});
app.post('/next', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit('slide', slide);
}
views/index.html
视图/index.html
This file is processed as a doTtemplate.
此文件作为doT模板处理。
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$('#slide' + curslide).css('left',0);
$('#next').click(function() {
$.post('/next');
});
});
var socket = io.connect('http://localhost:70');
socket.on('slide', function(slide) {
$('#slide' + curslide).animate({left:-700}, 400);
$('#slide' + slide).css('left',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copy these two files into a folder, then run
将这两个文件复制到一个文件夹中,然后运行
$ npm install express socket.io dot
$ node app.js
and navigate to http://localhost:70
in several different windows, then see the magic.
并导航到http://localhost:70
几个不同的窗口,然后看看魔术。
回答by MikeWyatt
Measure the elapsed time between sending the request and getting back the response. Then, divide that value by 2. That gives you a rough value of one-way latency. If you add that to the time value from the server, you'll be closer to the true server time.
测量发送请求和返回响应之间经过的时间。然后,将该值除以 2。这将为您提供单向延迟的粗略值。如果您将其添加到来自服务器的时间值,您将更接近真实的服务器时间。
Something like this should work:
这样的事情应该工作:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open('HEAD', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesting aside: John Resig has a good articleon the accuracy of Javascript timing.
It shouldn't cause a problem in this case, since you're only concerned about your time being off by ~1 second. A 15 ms difference shouldn't have much effect.
有趣的是:John Resig 有一篇关于 Javascript 计时准确性的好文章。
在这种情况下它不应该引起问题,因为您只关心您的时间差了大约 1 秒。15 毫秒的差异应该不会产生太大影响。
回答by David Braun
I'm glad you found a satisfactory answer to your question. I had a similar need to synchronize the browser with the server's clock and was determined to achieve it with better than 1 second accuracy like you were. I've written code to do this and am posting this answer here in case anyone else needs the solution too.
很高兴您为您的问题找到了满意的答案。我也有类似的需求,需要将浏览器与服务器的时钟同步,并决心像您一样以超过 1 秒的精度实现它。我已经编写了代码来执行此操作,并在此处发布此答案,以防其他人也需要该解决方案。
The code is called ServerDateand is freely available for download. Here's part of the README. Notice that I achieved 108 ms precision in my example:
该代码称为ServerDate,可免费下载。这是自述文件的一部分。请注意,我在示例中达到了 108 毫秒的精度:
You can use ServerDate
as you would use the Date
function or one of its
instances, e.g.:
您可以ServerDate
像使用该Date
函数或其实例之一一样使用,例如:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
There is also a new method to get the precision of ServerDate's estimate of the server's clock (in milliseconds):
还有一种新方法可以获取 ServerDate 对服务器时钟的估计精度(以毫秒为单位):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
You can see the difference between the server's clock and the browsers clock, in milliseconds:
您可以看到服务器时钟和浏览器时钟之间的差异,以毫秒为单位:
> ServerDate - new Date()
39
回答by Chris
I'm extensively using the COMET pattern here for my real time web application.
我在这里广泛地将 COMET 模式用于我的实时 Web 应用程序。
To use that in your case you'd need the clients to open an AJAX request to the server and wait for an answer. As soon as it comes the client has to change slides.
要在您的情况下使用它,您需要客户端向服务器打开 AJAX 请求并等待答案。一旦出现,客户就必须更换幻灯片。
On the server you have to hold back all answers till it's time to change slides. (You could be more advanced and delay afterwards on the client for the same time each, but that's most likely not necessary). I can't show you sample code for that here as I don't know what's available to you.
在服务器上,您必须保留所有答案,直到需要更换幻灯片。(您可以更先进,然后在客户端上延迟每个相同的时间,但这很可能没有必要)。我无法在这里向您展示示例代码,因为我不知道您可以使用什么。
So you are effectively creating an orchestra where the server plays the conductor and all clients are listening to him.
因此,您正在有效地创建一个管弦乐队,其中服务器演奏指挥而所有客户端都在听他的演奏。
Timing is then determined by the ability of the server to answer the requests at (nearly) the same time plus the network latency.
Usually the clients should be in the same part of the network so latency might be very similar - and the absolute value doesn't hurt here, only the variation.
然后由服务器在(几乎)同一时间响应请求的能力加上网络延迟来确定时间。
通常客户端应该在网络的同一部分,所以延迟可能非常相似 - 绝对值在这里不会受到影响,只有变化。
And there might also be an additional trick helping: don't change the slides with a hard replacement, blend them. This will blur the change so that the eye can't catch the little timing differences that you'll always have.
并且可能还有一个额外的技巧有帮助:不要用硬替换来改变幻灯片,而是将它们混合。这将模糊更改,使眼睛无法捕捉到您始终拥有的微小时间差异。
(If you can't have the server playing conductor you're likely to have to use the solution by MikeWyatt - probably with a few requests and averaging the result, depending on the network setup. In a LAN one request might be enough, going over the full internet a bit over averaging won't hurt...)
(如果你不能让服务器扮演指挥你可能不得不使用 MikeWyatt 的解决方案 - 可能有一些请求并平均结果,具体取决于网络设置。在 LAN 中,一个请求可能就足够了,去在整个互联网上稍微超过平均不会伤害......)
回答by Bergi
You can't really sync up with the server. Measuring the time your server request needs (as MikeWyatt suggested) is not a good indicator on the latency.
您无法真正与服务器同步。测量您的服务器请求所需的时间(如 MikeWyatt 建议的)并不是延迟的好指标。
Only your server knows when he responds a request. Therefore, it should send that information back with the answer. With Date.now() - new Date(timestringOfServerResponse)
you can measure the latency exactly. Yet I'm not sure why you would need that value.
只有您的服务器知道他何时响应请求。因此,它应该将该信息与答案一起发回。有了Date.now() - new Date(timestringOfServerResponse)
你可以精确地测量延迟。但我不确定你为什么需要那个值。
To sync an app between mulitiple devices, the server should send them which action to perform when. The "when" should not be "as soon as you get my response", but a exact timestamp. As far as the system clocks of your devices are accurate and synced (they usually are), the app will run its methods synchrounously, because it knows what to happen when (or at least: what should have happened then, and it can interpolate what to happen "now").
要在多个设备之间同步应用程序,服务器应该向它们发送要执行的操作。“何时”不应该是“一旦你得到我的回应”,而是一个确切的时间戳。只要您设备的系统时钟是准确和同步的(它们通常是),该应用程序将同步运行其方法,因为它知道何时会发生什么(或至少:当时应该发生什么,并且它可以插入什么发生“现在”)。