前言: 最近在使用 VSCode 调试 web 程序时,遇到一些问题,当时不知道如何是好。所以决定抓看来看一看,然后一顿操作猛如虎,成功安装了抓包软件 – Fiddler Classic。我并没有使用 Postman 这种重量级的 HTTP 测试软件,而是直接使用了 VSCode 的插件 – REST Client。然后,我发现在 Fiddler 里面居然无论如何都是看不见这个插件发出来的数据包,或者更直接一点,看不到 VSCode 的包。经过一番在互联网上的搜索,最后发现了需要在 VSCode 中配置代理才行。不过,正是这番折腾也让我重新思考起了以前一个困惑我的问题,抓包软件的工作原理是什么?我曾经对这个很好奇,知道了它是通过代理来实现的,但是这种浅显的理解,遇到问题时还是会显得束手无策。下面就让我们再去探索一番,如果每一次探索都能获取到更深一层的理解,那便是最好不过的事情了。所以学习更像是一种螺旋上升的过程,不是一蹴而就,当然了如果不持续努力就会一直原地踏步,甚至缓慢下降。
VSCode 抓包配置
先看看一下这个问题的解决方案吧!这里先来一个示例,查看指定QQ号的头像,这是一个在网上公开可查询的 API,我相信大家的为人,所以这里我没有使用自己的 QQ 号。
浏览器抓包测试
然后在 Fiddler 中可以查看该请求,注意我在上面禁用了缓存,防止因为缓存导致看不到图片了。这里可能数据包太多了,导致看不过来,可以在抓到数据包后暂停抓包或者在上面的列中进行搜索或者过滤。现在是浏览器的包,这是一切正常的,下面我们来使用 VSCode 的 REST Client 插件来请求这个接口试试。
VSCode 抓包测试
首先在 VSCode 中访问,这是没有问题的,可以正常请求到数据,而且它还很贴心的直接渲染成图片了。
这里我设置只抓取非浏览器的数据包,不然数据包太多了,看起来很麻烦。
然后你猜怎么着?我本来是想演示,VSCode 不配置是抓不到包的,结果今天在我自己的电脑上,它就正常了。因为我之前是在其它电脑上操作的,换了一个环境,结果它就正常了。不过现在我想要的是不正常的结果。不过,我现在对这一块也有了解,不至于像上次一样手足无措了。既然,正常了,那就直接去看下一部分,待会再聊这个话题。
注意:因为我使用的 VSCode 版本较新,可能是新版的配置变更了。所以我贴出来我的版本,如果你的表现或者配置与我不一致,可以考虑更新到同一版本或者更高的版本。
Fiddler 抓包的原理
当我们正常的访问网络服务时,忽略其它的因素的存在(专业一点应该叫透明),我们是直接访问远程服务的。然后开启代理之后,我们是通过代理来访问远程服务的,即我们先把请求发给代理,代理再去请求远程,然后把响应信息返回给应用程序。我这里使用单箭头,因为我只是画了请求的过程,哈哈,似有不妥,但是你理解就好了。
注意:在 Fiddler 中讨论的是 HTTP 代理,它应该是使用最广泛的了,不过代理不止只有 HTTP 代理。
所以,现在我们去看看 Fiddler,启动它之后,它会开启一个代理服务器,并监听 8888 端口。我们打开菜单,查看 connections 栏,可以看见下面这个弹窗。
Fiddler Classic can debug traffic from any application that accepts a HTTP Proxy. All WinINET trafic is trouted through Fiddler Classis when “File > Capture Traffic” is checked.
这里最重要的就是这个第一句了:Fiddler Classic 可以调试接受 HTTP 代理的任何应用的流量。这里的关键字是接受,所以那就有人可以 reject ,拒绝 喽!其它的信息,这里忽略不去理会,因为并不属于这里的重点。
注:有些软件因为一些原因,不会走代理,例如有道词典,我就没有抓到它的包(我上次见的说法是它不使用 HTTP 协议,也有可能是它不走代理,不过具体不了解,表现就是代理看不到它的请求信息)。
也就是说,Fiddler 开了一个代理服务器,所有的应用程序(接受 HTTP 代理)都会它发送请求,然后由它进行中转发送,并接受响应数据,然后再转给发送者。那你有没有想过,其它程序为什么会知道 Fiddler 的代理呢?或者它们为什么会相信一个未知的软件呢?这其实是一个很有趣的问题。因为它们并不知道 Fiddler,也不会去关心它的存在(当然了,得排除哪些不想让你调试它们的软件)。
答案其实很简单,系统代理,操作系统都是会支持网络代理功能的。但是它并不是真正工作的代理软件,只是告诉其它软件,这里启用了系统代理,因为是操作系统,所以应用软件都能知道系统启用了代理功能。
而 Fiddler 就是真正工作的 代理软件,如下图的 Gateway(网关)这里的截图。
然后去 Windows 中查看系统代理,不同版本的系统截图可能不一致,不过大体上也不会相差太大的。我现在使用的是 Window11,下面是我的系统的截图。不过这里居然没有设置端口而是直接在URL中指定了,可能这样也是可以正常工作的吧。所以,你现在应该大致上明白了吧。因为操作系统是支持代理这个功能的,而且这也是必须由操作系统支持的,因为通过代理上网是非常必要的功能。既然系统支持了,那么软件就不可能不支持了,否则用户启用了代理,而软件不走代理,那么就会导致软件的行为和用户的预期不一致了,这是 BUG呀!有些工作是必须通过代理上网的,代理的运用是很广泛的,例如企业内部的 VPN 服务。
现在的浏览器都践行这种 KISS 原则,它会自动检测到代理开启,所以不需要用户去手动配置了。不过,对于我们这些非普通的用户,还是有必要了解代理的工作原理的。不然,如果出现问题了,你就会变成真正的傻瓜了。
注:KISS(Keep It Simple and Stupid)傻瓜原则
VSCode 的代理配置
打开下面这个界面,然后搜索 http: proxy
,可以看见下面这些配置。最上面的这个是代理配置,如果没有设置的话,它会继承 http_proxy
和 https_proxy
这两个环境变量,不过我也没有设置它们,并且查看了确实没有。然后是下面的这个配置 Http: Proxy Support
,它下面写着它的作用:Use the proxy support for extensions
. (对插件使用代理支持),因为我这里没有在 VSCode 中开启代理,看起来是这个配置的作用。
注意:这里可以看到我改了 Http: Proxy Authorization
,这个是代理认证相关的属性,我只是点击进去,它给配置了默认的 null 值,然后我就退出来了。不过,这里我也没有使用需要认证的代理,所以也没关系的。
下面我把这个配置给关掉再试一试!这个默认的配置项 override
的作用就是,对插件启用代理支持,覆盖默认的请求选项。现在改成 off
,禁用插件的代理支持。然后多次请求那个查看 QQ 头像的接口,可以发现已经无法再看到相应的请求了。这里现在设置了只查看 VSCode 的请求数据。这个 code 进程就是 VSCode。所以,以后如果遇到 VSCode 无法抓包的问题就开启下面这个选项就行了(注意VSCode的版本)。下面使用 GIF 来演示关闭代理支持之后的抓包情况。
注意:因为我的 VSCode 版本较新,如果是使用的老的版本,可能需要手动配置上面的 Http: proxy
,不过要小心,如果你之后关闭了代理软件,你的 VSCode 就无法联网了!!!
代理软件最常见的问题
下面举一个例子,这应该是代理软件最常出现的问题,大部分人应该都遇到过的 – 代理软件的非正常关闭。让我们先把 Fiddler 关闭,然后查看系统代理,正常情况下它是关闭的,现在让我们手动打开它,配置和之前一样。然后,接下来会发生什么呢?
服务中断,你的浏览器就无法访问任何网页了,就像下面这样。 不过某些软件,例如 QQ 之类的会继续正常工作,因为它不走代理(它用的不是 HTTP 协议,不过这种软件都是很复杂的了,只是说通讯的部分不使用)。这个很好解释,就像上面那那个图一样,软件把请求发给系统代理,就是 Fiddler 启动的代理服务器,然后这个服务器早就关闭了,所以所有的请求都根本没有人接收,自然就会报错了。解决办法那自然就有两个了,关闭系统代理或者重新开启 Fiddler (或者任何你使用的代理软件)。只要是用过代理软件的人,应该都会踩过这个坑,哈哈。
TIM(QQ) 设置代理:
小小的扩展
你有没有想过到底代理是怎么工作的呢?带着这个疑问,让我们进行一点微不足道的探索吧!这里我来提供一个小小的示例程序,跟着我一起去看看吧,不要弄那么复杂了,这个代码就是随便写的,展示一下简单的原理。
package main
import (
"log"
"net"
)
const RSP = "HTTP/1.1 200 OK\r\n" +
"content-type: application/json; charset=utf-8\r\n" +
"content-length: 39\r\n" +
"server: CrazyDragon\r\n" +
"\r\n" +
"{\"rsp\": \"Hello, I am CrazyDragonProxy\"}\r\n"
func main() {
// 监听本地的 9999 端口
server, err := net.Listen("tcp", "localhost:9999")
if err != nil {
log.Fatal(err)
}
// 读取数据的切片
data := make([]byte, 1024)
for {
if conn, err := server.Accept(); err == nil {
if n, err := conn.Read(data); err == nil {
log.Printf("%s", string(data[:n])) // 打印接受到的数据
conn.Write([]byte(RSP)) // fake response! 随便弄一个假的响应糊弄了事
conn.Close()
} else {
log.Fatal(err)
}
} else {
log.Fatal(err)
}
}
}
先开启代理设置,配置如下:
然后就按照这个 GIF 中做的即可,注意我虽然开了代理,但是代理后面并没有软件,所以此时 VSCode 的插件是无法正常联网的,因为代理软件没开!!!然后我启动了代理软件(我自己写的垃圾,不过它已经可以符合一个正常的代理了),然后这个插件就可以正常工作了。
注意: 有时候开启了代理,但是 VSCode 还是不走代理,并且配置也是正常的。可以考虑重启或者 Reload Window
刷新一下,因为配置可能没更新,VSCode 没有更新系统代理的变更。
在 VSCode 中演示:
在浏览器中演示:
注:这里有一个小问题,似乎第一次请求会卡住,不知道为什么?所以我强制刷新一下。
下面这个是打印输出的部分信息,中间的方框内是接收到的访问 QQ 头像的请求,这里出现了很多 CONNECT 请求,它也是 HTTP 方法的一种,不过它不会用在 WEB 请求中。而是专门用来给 HTTPS 进行代理的,HTTPS 代理曾经也是一个难题(TLS),这就是解决方案,不过我也就了解这么多。我这里只是一个简单的 HTTP 代理,它尝试来建立连接,当然是不会成功了,哈哈。
所以如果访问 https 网址,效果就是这样的,这里肯定是 CONNECT 建立失败了,就是有关于 TLS 这方面的东西。所以我测试使用的是 http 的网址。
什么决定了使用代理
为什么有的可以选择走代理,有的可以选择不走代理呢?这是由什么决定的呢?如果你有过网络爬虫的经验,应该就能明白了。编程语言的 HTTP 客户端都是可以配置使用代理的,就以为 Go 为例:
因为代理是很重要的功能,所以它是一个 HTTP 客户端必不可少的功能。不过,上面也说了可以选择 no_proxy
(禁用代理),所以有些软件即使使用 HTTP 协议,你也是抓不到的,因为它根本就不使用代理。风过留声,雁过留痕。既然不从代理走,代理也就根本无法感知到它们的存在了。
总结
有趣的探索结束了,我确实感觉自己收获了不少,也把以前的一些知识串联起来了。希望各位读者也能从中有所收获。
参考链接
If I have been able to see further, it was only because I stood on the shoulders of giants. 如果说我看得比别人更远些,那是因为我站在巨人的肩膀上。
HTTP 代理原理及实现
技术分享| HTTP 代理
部分APP无法代理抓包的原因及解决方法
大厂app抓不到包怎么办?这篇文章告诉你答案!