如何通过浏览器确定远程服务器的延迟

17

我运行了几个游戏隧道服务器,希望有一个页面,客户端可以对所有服务器运行ping测试,并找出哪一个响应最快。据我所见,JavaScript似乎没有合适的方法来实现这一点,但我在想,是否有人知道如何在flash或其他客户端浏览器技术中实现这一点呢?


我对你的解决方案非常感兴趣。有没有可能得到一些结果?提前谢谢! - Theo.T
对我来说最有趣的答案是Eliram的答案(https://dev59.com/9nRB5IYBdhLWcg3wz6Wd#575542),但StackOverflow已经为我选择了答案。虽然如此,我并没有进一步追求这个项目。我不是一个Flash开发者。 - Mladen Mihajlovic
9个回答

18

大多数小程序技术,包括JavaScript,都实施了同源策略。可以通过使用onload事件处理程序动态添加DOM元素(例如图像)并收集时间信息。

伪代码:

for (server in servers) {
  var img = document.createElement('IMG');
  server.startTime = getCurrentTimeInMS();
  img.onload=function() { server.endTime = getcurrentTimeInMS(); }
  img.src = server.imgUrl;
}

然后等待适当的时间并检查每个服务器对象的时间。如果需要,请重复此操作并计算平均值。我不确定您可以期望什么样的准确性。

缺点:

  • 您可能使用了错误的工具来完成这项工作。浏览器并没有为这种应用程序进行优化。
  • 它可能相当不准确。
  • 如果您请求的资源已被缓存,它将无法给出您想要的结果,但是您可以通过每次更改网址来解决这个问题。
  • 与普通ping相比,带宽占用率高。使图像变小,例如一个spacer.gif文件。
  • 计时不仅取决于远程服务器的延迟,还取决于该服务器的带宽。这可能是一种更或者更少有用的度量标准,但重要的是要注意,它不仅仅是延迟。
  • 您需要能够从各个服务器提供HTTP请求,并且至关重要的是,每个服务器都应该提供完全相同的资源(或相同长度的资源)。服务器上的条件可能会影响响应时间,例如如果一个服务器正在压缩数据而另一个服务器没有。

使用 new Image() 代替创建整个 DOM 对象。 - Georg Schölly
@gs:使用new Image()有什么优势吗? - Mr. Shiny and New 安宇
所有需要下载某些东西的解决方案的问题在于,它们实际上测试的是网络服务器的速度,而不是网络连接的延迟。因此,这并不是真正的反映,特别是对于我需要非常特定的网络延迟速度的情况。 - Mladen Mihajlovic
@Mladen Mihajlovic:这是您正在使用的技术的限制。大多数小程序技术不允许您与任意服务器建立任意TCP/UDP连接。这就是您所需要的。 - Mr. Shiny and New 安宇
似乎在Flash中是可能的(请参见下面的答案)。唯一的问题是我完全不懂Flash :) - Mladen Mihajlovic

9
在向服务器发起调用之前,记录Javascript时间:
var startTime = new Date();

从服务器加载图像:

var img = new Image()
img.onload = function() {
    // record end time
}
img.src = "http://server1.domain.com/ping.jpg";

请求完成后,立即记录时间。(当然,假设请求没有超时。)

var endTime = new Date();

您的延迟为毫秒:

var ping = endTime. getTime() - startTime.getTime();

6
你需要的只是连接开始到第一次状态变化的时间...
function getPing() {
  var start;
  var client = getClient(); // xmlhttprequest对象
  client.onreadystatechange = function() {
    if (client.readyState > 0) {
      pingDone(start); //处理ping
      client.onreadystatechange = null; //移除处理程序
    } 
  }
start = new Date(); client.open("HEAD", "/ping.txt"); //静态文件 client.send(); }
function pingDone(start) { done = new Date(); ms = done.valueOf() - start.valueOf(); alert(ms + "毫秒的ping时间"); }
function getClient() { if (window.XMLHttpRequest) return new XMLHttpRequest();
if (window.ActiveXObject) return new ActiveXObject('MSXML2.XMLHTTP.3.0');
throw("没有可用的XMLHttpRequest对象."); }

只有在ping托管网页的服务器时才能正常工作,而不能用于ping其他服务器。 - Mr. Shiny and New 安宇

4
这里有一个<iframe>的方法:


(来源:magnetiq.com)

创建一个表格(不一定是字面意义上的<table>),其中包含两列。第一列将保存服务器名称(以及可能的链接)。第二列具有从相应服务器加载探针文档的 iframe。每个探针文档在初始获取请求时执行此操作:

  1. 获取当前系统时间
  2. 通过将系统时间作为查询参数,执行重定向(302)到第二个探测文档
  3. 第二个探测文档读取当前系统时间,并计算与传递给它的初始读数的差值,然后以大号字母显示。这个差值将是服务器响应客户端重定向响应所需的时间加上客户端进行第二次请求到重定向目标所需的时间。虽然它不完全是“ping”,但它是客户端相对延迟的可比度量。事实上,它是从服务器到客户端的“反向ping”。

您将使用iframe而不侵犯同域策略,因为根本没有尝试操纵iframe内容。播放器将仅仅通过眼睛看到值,您将依靠用户瞥一眼数字并点击最合适的服务器链接。


3

任何发起HTTP请求的操作(像大部分回答中提到的)通常会测量出至少是正常ping延迟两倍的延迟,因为你需要至少进行三次握手和终止数据包(而不是一次)(两个往返而不是一个)。如果你进行HTTP请求,请尽量减少头信息。由于服务器过于啰嗦或客户端上的cookie等,过长的头信息可能会增加额外的往返,从而影响你的测量结果。

正如Cherona所指出的,如果你已经与服务器建立了一个活动的HTTP 2连接,或者服务器使用HTTP 3协议,则情况可能不同。

最准确的选项是对每个服务器打开一个websocket连接,并测量发送微小消息并接收微小响应所需的时间(在连接建立后)。


1
这对于HTTP2和HTTP3来说并不是真的。 - Cherona
好的观点!编辑后加入了HTTP 2、HTTP 3和Websockets的提及。 - Peter Burns

2

如果你想在客户端运行某些东西,由于安全原因,我不确定这是否可能。

也许你最好的选择是使用Java小程序 - 但同样需要根据本地安全策略进行检查。

如果我试图考虑一些JS中的黑客技巧来做到这一点,也许你可以尝试发送一个异步请求,并使用回调函数来测量花费的毫秒数 - 但这只是我的想法。


我知道一种方法是通过发送HEAD请求来实现,但我更希望能够使用Flash来完成它。 - Mladen Mihajlovic
Java applets只允许与它们的源服务器进行通信。 - Magnar
如果Applets被签名并接受,那么更宽松的安全设置是允许的。或者至少过去是这样的。我上次见到一个这样的是在Netscape 4上。 - Zan Lynx

2

在Flash中测量服务器响应时间并不难。

Flash在访问远程服务器之前必须请求策略文件。 这种策略文件的默认位置位于服务器的根目录下:/crossdomain.xml

(您可以轻松找到有关crossdomain文件格式的信息)

既然无论如何都需要这样的文件,为什么不使用它来测量服务器响应时间呢?用getTimer()加载文件本身并测量所需的时间即可。

这将为您提供有关HTTP连接的良好估计。

但是,如果您处理游戏服务器,则可能希望直接检查TCP连接的速度。为此,您需要使用flash.net.Socket 您还需要先运行以下命令请求策略文件: Security.loadPolicyFile("xmlsocket://server.domain.com:5342");

其中5342表示服务器端口号,服务器应该以正确的XML策略字符串进行响应。 建立套接字连接后,任何请求/响应都将让您测量不同的服务器响应时间。


这听起来非常有前途。你有一些代码或者一个示例的Flash“小程序”可以让我看看吗?我自己不是ActionScript程序员。 - Mladen Mihajlovic

1
“文件 ping” 问题在于您将评估 HTTP 服务器响应,而您提供的游戏目标资源可能具有非常不同的行为和因此不同的延迟。
只是突发奇想,也许根据实际情况甚至不现实: 但是,制作一个基于服务器在游戏过程中通常执行的一系列任务的服务器脚本是否很有趣(例如打开 RTMP 连接、检索信息、发送回来)。根据服务器总数,您几乎可以同时打开它们,并定义第一个响应为胜利者(减去客户端独立处理每个查询所需的时间)。
当然,这是服务器方面相当昂贵的方法,但至少您希望得到可靠的结果(服务器和网络延迟总和)。即使需要几秒钟进行评估,这也只是整个愉快游戏过程的一小部分。

1

根据 @Mr. Shiny 和 @Georg Schölly 的回复,这里提供了一个完整且有注释的例子。

为了测试,请按照以下顺序将代码复制并粘贴到空的 .html、.php 或其他兼容文件中。

在开始获取之前,记录当前的 JavaScript 时间。 使用 new Date(),我们创建一个新的日期对象,其中包含当前的日期和时间。

<script type="text/javascript">

var startTime = new Date();

现在让我们创建一个HTML对象图像,仍然没有源,并将其属性分配给变量img。
var img = new Image();

下一步是在图像中放置一个源。.src 反映了 src html 属性。 重要!如果可能的话,请将 img.src 指向非常小且轻量级的图像文件,最好小于 10KB。
为了防止缓存,在 .png 扩展名之后添加了一个随机参数。
var random_string = Math.random().toString();
img.src = "http://static.bbci.co.uk/frameworks/barlesque/5.0.0/orb/4/img/bbc-blocks-dark.png" + "?" + random_string;

现在我们可以调用函数,它将会在图片加载后运行,因为使用了 .onload 方法:
img.onload = function() {
    var endTime = new Date();
    var ping = endTime. getTime() - startTime.getTime();
    alert(img.src + " loaded in " + ping + " ms");
}
</script>

在函数内部,我们有一个变量endTime,它接收源图像加载后的日期时间。

最后,ping变量接收初始时间减去最终时间。警告弹出窗口显示结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接