专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

Web 前端思考题:如何获取往返数据包的 TTL

ins518 2025-06-10 15:36:04 技术文章 4 ℃ 0 评论

1. 前言

Web 前端思考题:如何获取往返数据包的 TTL

注意,这里说的是「往返」,即去程和返程,也就是「服务端」收到「客户端数据包」的 TTL,和「客户端」收到「服务端数据包」的 TTL。

注意,这里说的是「接收」,毕竟发送时的 TTL 毫无意义,服务端固定已知,客户端通常是 64 或 128(和操作系统相关)。

这个问题的初衷,是想通过 JS 检测用户和服务器之间的往返路由数。虽然大多情况下往返链路相近,但也存在差异较大的情况。例如访问某些香港服务器,去程电信直连,而返程则会到日本绕一圈,链路差异非常大。

2. 去程

去程 TTL 很容易获取,直接读取 Web 服务器的 socket 即可(例如通过 getsockopt)。或者用 raw socket/libpcap 抓包也不难实现。

3. 返程

返程 TTL 就没那么容易获取了。

也许你会说,可通过服务器 traceroute 反查。这种方案虽然可行,但并不准确。如今用户几乎都在内网中,反查只能到用户的公网路由器,内网的路由数仍无法获取。而且反查效率很低,需要发不少数据包。

但用 JS 读取数据包 TTL 更不可行,毕竟浏览器功能十分有限。抓包这种操作,想都不用想;而 getsockopt 这类高权限 API,浏览器显然不可能提供。因此我们得另辟蹊径。

4. 思路

设想下,假如客户端能把某个「收到的数据包」封装在「另一种数据包」的内容里回传给服务器,那我们就可以在服务器上获取返程信息了。

事实上,这种情况是存在的!「ICMP 端口不可到达」协议专做这事。

那么,客户端在什么情况下会触发这种 ICMP?只要浏览器在收到服务器数据包之前断开连接即可。之后数据包达到时,由于操作系统找不到目标端口对应的连接,只能丢弃该包,同时回复一个「ICMP 端口不可到达」告知服务器。

该 ICMP 的内容部分,正是被丢弃包的网络层和传输层头,包含了我们想要的返程信息。

因此我们的核心思路:

  1. JS 向服务器发包
  2. 服务器稍作延迟,回复任意内容
  3. JS 在收到包之前释放连接
  4. 服务器抓取 ICMP (type=3, code=3) 类型的包

由于 TCP 连接是操作系统维护的,JS 难以精确控制释放时间,因此我们使用更简单的 UDP。通过 WebRTC 即可实现 UDP 连接的创建和关闭。

并且 TCP 是有状态的,连接断开后 NAT 会删除条目,服务器返回的数据包可能进不了内网。而 UDP 是无状态的,本地关闭连接对 NAT 毫无感知,此后一段时间里数据仍可进入内网。

5. 实现

实现比思考简单得多,这里就不讲解了。

需要注意的是,JS 向服务器发送 UDP 包时需携带一个 uuid 数据,用于之后获取数据时的关联。

6. 局限

不过并非所有情况下都能成功获取,例如有些操作系统禁用了端口不可到达的响应,有些运营商会丢弃这类 ICMP 包。

如果演示页面显示 fail,那就是无法获取了。(如果是 network err 可能测试服务关了~)

当然,本文纯属开脑洞而已,顺便分享一点点网络小知识~

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表