一、上网慢,为什么查不到病根?

很多人都有过这样的经历:家里宽带明明装的是千兆,测速软件上也显示满速,可打开某个网页就是慢得让人抓狂。换浏览器、重启路由器,折腾半天也没用。问题到底出在哪儿?

其实,网络访问就像快递送包裹。你输入网址,类似写下收件人姓名,但快递员真正想找到收货点,需要的是详细地址。这个地址就是IP地址。而把“收件人姓名”翻译成“详细地址”的,就是DNS服务器。这个翻译动作,叫DNS解析。

如果这个翻译过程卡壳,哪怕网速再快,浏览器也得干等着。更快连接的基础是“第一步找对门”,而DNS就是那个帮你找门的人。更气人的是,这种慢常常藏得很深,因为你表面上看到的还是“正在连接服务器”,根本不知道其实是DNS在磨洋工。

幸运的是,我们有一把很趁手的“放大镜”——Wireshark,它能把网络流量变成可见的报文,让我们看个清清楚楚。

二、用Wireshark抓出DNS的请求和响应

Wireshark是业界非常流行的网络协议分析工具。它能让电脑上每一个经过网卡的数据包“现出原形”。要分析DNS,我们先要知道DNS的默认端口是53。所以,最基本的操作就是让Wireshark只显示和53号端口有关的包。

2.1 先简单抓一次包

打开Wireshark,选中你要抓包的网卡,然后在顶部的显示过滤器栏里输入:

udp.port == 53

按回车后,画面里立刻只剩下UDP协议上端口为53的报文。你会看到很多条记录,有向上发的请求,也有向下发的响应。不要慌,我们只是需要一个“现场录音”。

如果你更喜欢终端操作,可以用tshark。tshark是Wireshark的命令行版本,功能几乎一样,更适合在Linux服务器上使用。下面这条命令会抓取30秒的DNS流量,保存到 dns.pcap 文件里:

# 指定网卡eth0,只抓udp端口53,保存到dns.pcap,持续30秒
tshark -i eth0 -f "udp port 53" -w dns.pcap -a duration:30

抓完包后,文件就留在了手边,之后可以随时回放、过滤。

2.2 从文件里读出关键字段

拿到 dns.pcap 之后,我们需要把里面的信息一层层剥开。tshark 强大的地方在于,它能直接把你想看的字段输出成一行,方便后续统计。比如下面这条命令,就输出了时间、源IP、目标IP、源端口、目标端口、是否是响应、查询域名、查询类型、解析结果:

# 读取dns.pcap,按预定字段打印每条DNS报文
tshark -r dns.pcap -T fields \
  -e frame.time \
  -e ip.src \
  -e ip.dst \
  -e udp.srcport \
  -e udp.dstport \
  -e dns.flags.response \
  -e dns.qry.name \
  -e dns.qry.type \
  -e dns.a

输出的每一行就是一条记录,比如下面这样(这里只是个示意):

2025-04-12 10:01:01.123456 192.168.1.5 8.8.8.8 12345 53 0 example.com 1 
2025-04-12 10:01:01.123789 8.8.8.8 192.168.1.5 53 12345 1  1 93.184.216.34

第一行里的 0 表示这是一个请求;第二行的 1 表示这是一个响应,且给出了解析结果。通过对比请求和响应出现的时间,我们就能估算出这一次DNS解析花了多久。

三、看懂查询类型,才知道人家问的是什么

DNS解析并不只是简单地把一个域名变成一个IP。根据应用的需求,DNS会携带不同“问题”,我们称为查询类型。最常见的是A记录,也就是IPv4地址。如果你访问的网站支持IPv6,浏览器还会发AAAA类型的查询。除此之外,还有CNAME别名、MX邮件交换等多种类型。

在Wireshark里,你可以点击报文的 Queries 区域,看到 Type: A (1) 这样的行。用tshark时,dns.qry.type 字段会直接输出数字。对应关系如下:

  • 1:A记录,IPv4地址
  • 28:AAAA记录,IPv6地址
  • 5:CNAME记录,别名
  • 15:MX记录,邮件交换
  • 255:ANY,所有记录

看到这里,你可能会问:知道查询类型有什么好处?举个例子,如果一个网页为了加载一张图片,中间发生了好几次CNAME跳转,那么浏览器就要先解析之前的别名,再解析真正的地址。每跳转一次就多一次请求,慢也就在所难免。

我们可以用tshark专门把CNAME请求筛出来,看看有没有“绕远路”的域名:

# 只看查询类型为5(CNAME)的DNS请求
tshark -r dns.pcap -Y "dns.qry.type == 5" -T fields \
  -e frame.time -e ip.src -e dns.qry.name

如果输出结果特别多,而且好几个域名之间互相跳转,那就说明这个网站使用了较长的重定向链路,解析延迟自然会被放大。

四、递归过程:一次网络寻亲记

DNS查询还有一种重要的分类方式:递归查询和迭代查询。我们平时发给本地DNS服务器的请求,一般是递归查询。本地服务器如果不认识这个域名,就会代替我们去问其他的DNS服务器,直到得到答案,然后再回答给我们。这个过程很像请朋友帮你找一位远房亲戚:朋友去问他的朋友,他的朋友再去问朋友的朋友,最后把联系方式带给你。

4.1 如何判断是不是递归

在报文里,递归请求的标记是 Recursion Desired,递归可用的标记是 Recursion Available。对应到tshark中,就是 dns.flags.recursion_desireddns.flags.recursion_available。我们可以在输出里加上这两个字段:

# 打印请求和响应,并标出是否递归要求/递归可用
tshark -r dns.pcap -T fields \
  -e frame.time \
  -e ip.src \
  -e ip.dst \
  -e dns.flags.response \
  -e dns.flags.recursion_desired \
  -e dns.flags.recursion_available \
  -e dns.qry.name

假设本地DNS服务器支持递归,你会发现它返回的响应里,recursion_available 的值是 1。但注意,我们只能看到与本地DNS服务器交互的那一段。如果本地DNS服务器内部访问了根服务器或顶级域名服务器,那这些流量不会出现在普通家用电脑的抓包里。要想观察完整的递归链路,你需要在本地DNS服务器上抓包,或者用专门工具模拟查询。

4.2 用tshark计算每次响应的耗时

我们要定位延迟,最终还是要看时间。Wireshark提供了一个很方便的字段:dns.time,它记录了从请求发出到收到响应所经过的时间,单位是秒。我们可以把响应报文里的耗时排个序,把最慢的几名揪出来:

# 找出响应时间最长的10个DNS解析
tshark -r dns.pcap -Y "dns.flags.response == 1" -T fields \
  -e dns.time -e dns.qry.name | sort -rn | head -10

结果的第一列就是耗时秒数。如果有一堆请求耗时超过100毫秒,而且对应着同一个域名,那问题就很明显了。

五、真实故障:网页转圈圈,到底卡在哪?

我们来看一个实际碰到的例子。用户反映“打开某个网站特别慢,但其他网站都正常”。在用户的电脑上用Wireshark抓包30秒,发现 slow.example.com 这个域名的DNS请求反复出现,而且响应时间多数在500毫秒以上。有一次,请求发出去后足足隔了800毫秒才收到响应,这明显不正常。

随后我们查看对应响应里的 dns.a 字段,发现返回的IP地址只有一个,但连接这个IP时,TCP握手一直在重传,始终连不上。这说明问题不只是DNS慢,而是DNS服务器返回的IP本身已经失效了。这时候,浏览器又重新向另一台DNS服务器发起查询,才拿到了一个可以使用的IP。整个过程就好像快递员拿到了一张旧地址单,跑错地方后又重新问路,浪费了大量时间。

这个案例告诉我们,分析DNS故障时,不能只看请求有没有响应,还得看响应里的内容是不是“靠谱”。如果响应很快但IP是坏的,那同样会让用户体验到“打开慢”。

六、怎么解决和避免DNS解析延迟

一旦定位到问题,后续的修复手段就有方向了。下面是几个常用的“急救措施”:

  • 第一,换更快的DNS服务器。很多公共DNS服务,除了速度快,还带了一些缓存优化和安全防护。
  • 第二,在路由器上启用DNS缓存。局域网内多台设备频繁访问同一域名时,缓存能大幅减少外部查询的次数。
  • 第三,开启DNS over HTTPS(DoH)做加密传输,让运营商不容易劫持你的DNS请求,也能减少被污染的风险。

这里顺便介绍一个关联技术:TTL。TTL全称是Time To Live,中文叫“存活时间”。DNS响应里会告诉本地缓存:这条记录你可以留多久。如果TTL太短,比如只有几十秒,那么每次访问都要重新去解析,无形中增加了延迟;如果TTL太长,当服务器IP变了,客户端还在用旧IP,又会引发一连串连接超时。用tshark能直接看到每条响应的TTL:

# 检查每个响应的TTL值,看看缓存策略是否合理
tshark -r dns.pcap -Y "dns.flags.response == 1" -T fields \
  -e dns.resp.ttl -e dns.qry.name | head -20

如果大量记录的TTL都是1秒或2秒,那说明这个网站的DNS解析策略过于激进,很容易造成频繁解析。这时候就要考虑在应用层面做一层缓存,以减轻对DNS的依赖。

七、应用场景与它的优缺点

用Wireshark分析DNS解析延迟,适用的场景其实很多。比如:

  1. 用户抱怨上网慢,但宽带测速正常,这时候DNS最值得怀疑。
  2. 某个域名时好时坏,怀疑是DNS劫持或缓存污染。
  3. 想要优化访问速度,需要了解当前DNS解析的耗时分布。

这种方法的优点很突出:首先,它能看到最原始的网络行为,没有中间层的“美化”。其次,Wireshark和tshark都是开源免费的,也支持在命令行下自动化运行,适合在服务器上快速收集证据。最后,分析结果非常直观,通过对字段筛选和排序,能很快锁定异常域名。

当然也有缺点。第一,抓包分析需要懂得一定的网络协议基础,不然面对密密麻麻的报文会一头雾水。第二,如果客户端使用了DoH这类加密DNS技术,抓包就只能看到TLS加密流量,无法直接看到DNS内容,这个时候需要额外解密或结合浏览器调试工具。第三,抓包本身会消耗一点系统资源,在大流量或者生产服务器上,不能任意长时抓包。

八、注意事项

在动手分析之前,有几条注意事项值得记在心里。

  • 分清UDP和TCP。普通的DNS大多走UDP,但如果响应数据太大,会切换到TCP。过滤器里不要只设置 udp.port == 53,必要时也要看看 tcp.port == 53
  • 抓包过滤器 -f 和显示过滤器 -Y 是完全不同的语法。-f 在抓包时就过滤掉了,拿不到被丢弃的包;-Y 只是把已抓到的包隐藏起来,数据还在。建议先用 -f 做粗过滤,再用 -Y 做细筛。
  • 注意观察最差的几次延迟,而不是只看平均。网页卡顿通常是由个别超时引起的,平均值反而会误导人。
  • 别忘了关闭电脑上的其他后台应用,不然抓到的DNS请求不一定是你要分析的网页产生的,干扰判断。

九、总结

DNS解析延迟虽然看不见摸不着,但它对上网体验的影响非常大。通过Wireshark,我们能把DNS请求和响应从网络流量中分离出来,看清查询类型、递归标志、响应时间和TTL等关键细节。遇到网页打开慢,先别急着抱怨网速,抓个包看看垃圾流量里的DNS字段,你会发现很多问题都能迎刃而解。Wireshark不是万能的,但用来定位DNS故障,它绝对是值得信赖的那把手术刀。