4_网络代理传输架构详解:从直连到专线
网络代理服务的性能取决于传输架构。本文解析三种主流架构(直连、中转、专线)的技术原理,涵盖 BGP、CN2 GIA、IPLC、IEPL 等关键技术,并基于使用场景提供选型策略。
网络代理服务的性能取决于传输架构。本文解析三种主流架构(直连、中转、专线)的技术原理,涵盖 BGP、CN2 GIA、IPLC、IEPL 等关键技术,并基于使用场景提供选型策略。
跟着 git clone 走一遍代理工具的每一道关——TUN 怎么抓包、fake-ip 怎么工作、Clash Verge "DNS 覆写" 开关到底覆写什么、三份真实订阅 DNS 配置对照。
第一篇的深挖篇——GFW 旁路注入怎么干活、fake-ip 把局域网整瘫痪怎么救、Navicat 直连域名怎么还原、fallback 三件套、TUN 三档力度、用脚本注入完整 dns 块。
整个演变史就是一部皇帝(皇权)与宰相(相权)之间长达两千年的“权力拔河赛”。理解了这条主线,你就能看懂很多历史事件背后的真正动因。
学习时,始终抓住“皇权 vs 相权”这条核心斗争线。所有制度改革,几乎都围绕这个展开。
三省六部有分权制衡的影子,但其根本目的是为了提高皇权统治的效率和安全,而不是限制皇权。所有权力最终都源于皇帝,也服务于皇帝。
这个划分最早起源于 18 世纪末的法国大革命。在当时的法国国民议会上:激进的、支持革命、希望彻底改变旧制度(推翻君主制和贵族特权)、主张建立一个更平等、更世俗化共和国的议员们,习惯性地坐在议会场的左边。保守的、希望保留传统、维护君主和教会权威、主张温和改革或维持现状的议员们,则坐在右边 。
繁衍,是刻在动物身体基因里最原始的一个欲望。
萨特曾言,人类是自由的,却常常通过“自欺”来逃避责任——我们说服自己“不想”结婚生子,只是因为恐惧生活的不确定性、经济负担和社会期望的枷锁。这种麻醉是暂时的自我保护,却可能在生命的后期酿成深刻的空虚。
年轻时做出的选择,逻辑上或可自圆其说,然人生的悲剧在于,我们常在信息不完整下做出不可逆转的决定。尤其对女性而言,生育机能的不可逆转,使得她们在“醒悟”时往往已无从补救。
费孝通先生说过生育是一个 “ 损己利人 “ 的事情,我们为什么要延续生命、做一个低回报的投资?答案在于:在亲密关系中寻觅意义,将“小我”融入“大我”的体验,本身就是一种对抗虚无、赋予生命以纵深感的强大力量。
人生永远不存在完美的准备时刻,正如李安在电影饮食男女里所说 “ 人生不能像做菜,把所有的料都准备好了才下锅 “。选择结婚并非盲动,只是在不完美的烹饪过程中,才能被赋予最真实、最热烈的味道。
我们在社交媒体和生活中看到了太多的婆媳关系的矛盾,却鲜少听闻发自肺腑的和解与感恩。对儿子和丈夫的争夺,不足以解释现代婆媳矛盾的核心。真正的引爆点,往往出现在第三代——孩子的降生。
祖辈对孙辈的照看,是一种源于血脉之亲的、自愿的付出。它是一份恩情,而非一份责任。媳妇认为婆婆带孙子是理所应当,她便剥夺了这份行为的恩情属性,将其贬低为一种必须履行的服务。如果婆婆将带孙延伸为对新家庭的掌控权,她亦玷污了这份恩情的纯粹。
现代家庭悲剧的根源,恰恰在于将恩情误读为责任。每个人都宣称为了孩子好,却在这场恩责不分的混乱中,互相感到自己的付出被漠视。
丈夫永远不要去做一个裁判,而是要与妻子共同组成一个阵线。双方必须全部完成对各自原生家庭的背叛,才能真正做到婆媳关系的破局。
真正的家庭哲学,应当是一种“树状”结构 🎄:
除了亚里士多德的第一性原理,我一直觉得世界上还些一个真理,使用上就像开了天眼生活。
就是按照你期待的美好去生活,将其从一种“愿望”深化为一套“主动创造现实”的行动哲学。
我命名为:第一美好原理。
不要因为懊悔消耗自己的能力,而是要把能量转化为解决问题的行动。火箭启动前永远在蓄力,不是在消耗。
我命名为:第一能量原理。
错误决策是一个老师,它的任务是“教会”你,而不是“惩罚”你。错误是老师,行动是解药。
我命名为:第一行动原理。

📌 比如你之前可能没有意识到,DNS 查询在基于 TCP 时效率有多低
📌 我们也许可以在几个小时里学会使用Wireshark软件,在几天里学会一个协议,但是思路的养成却需要经年累月的锻炼。
📌 Wireshark 是最流行的网络嗅探器之一,能在多种平台上抓取和分析网络包,比如Windows、Linux和Mac等。它的图形界面非常友好,但如果你觉得鼠标操作不够有腔调,也可以使用它的命令行形式——TShark。
📌 很显然,Wireshark 并不能帮我们变成网络新贵,但它对技术上有所追求的工程师来说,有着金钱难以衡量的价值。用它来辅助学习,可以更深入地理解网络协议;用它来排查故障,可以更快地发现问题。假如你是团队中唯一掌握 Wireshark的网络工程师,这个看家本领非常有助于你保持大牛地位。在同事们手足无措时,你可以用最快的速度摆平,然后平静地说一句:“问题解决了,我先去泡杯咖啡。”
📌 技术类知识就是这样,如果你从最简单的地方开始动手操作,接下来就如鱼得水;如果从一开始只依靠冥想,到后面就会走火入魔。
📌 容易理解是最难做到的一点。传说白居易写完一首诗,必定先请不识字的老太婆品鉴,一直要修改到老太婆听懂为止
📌 服务器B通过ARP广播查询默认网关192.168.26.2的MAC地址。为什么我ping的是服务器A的IP,B却去查询默认网关的MAC地址呢?这是因为B根据自己的子网掩码,计算出A属于不同子网,跨子网通信需要默认网关的转发。而要和默认网关通信,就需要获得其MAC地址。
📌 为什么这些MAC地址的开头明明是“00:50:56”或者“00:0c:29”,Wireshark上显示出来却都是“Vmware”?这是因为 MAC 地址的前 3 个字节表示厂商。而 00:50:56 和 00:0c:29 都被分配给Vmware公司。这是全球统一的标准,所以Wireshark干脆显示出厂商名了。
📌 B回复了A的ARP请求,把自己的MAC地址告诉A。这说明 B在执行ARP回复时并不考虑子网。虽然ARP请求来自其他子网的IP,但也照样回复。
📌 B先把ping请求交给默认网关,默认网关再转发给A。而A收到请求后直接把ping回复给B,
📌 在 Wireshark 上可以这样抓到包头:单击菜单栏上的 Capture–>Options,然后在弹出的窗口上定义“Limit each packet to”的值。我一般设个偏大的数字:80字节,也就是说每个包只抓前80字节。这样TCP层、网络层和数据链路层的信息都可以包括在内
📌 如果问题涉及应用层,就应该再加上应用层协议头的长度。如果你像我一样经常忘记不同协议头的长度,可以输入一个大点的值。即便设成200字节,也比1514字节小多了。
📌 用tcpdump命令抓包时可以用“-s”参数达到相同效果。比如以下命令只抓eth0上每个包的前80字节,并把结果存到/tmp/tcpdump.cap文件中。[root@server_1 /]# tcpdump -i eth0 -s 80 -w /tmp/tcpdump.cap
📌 设置Capture Filter之前务必三思,以免把有用的包也过滤掉,尤其是容易被忽略的广播包。当然有时候再怎么考虑也会失算,比如我有一次把对方的IP地址设为filter,结果一个包都没抓到。最后只能去掉filter再抓,才发现是NAT(网络地址转换)设备把对方的IP地址改掉了。
📌 一篇文章不可能涵盖所有技巧,本文就到此为止。最后要分享的,是我认为最“笨”但也是最重要的一个技巧——勤加练习。只要练到这些技巧都变成习惯,就可以算登堂入室了。
📌 传输层:这一层用到了TCP协议。应用层所产生的数据就是由TCP来控制传输的。点开TCP层前的“+”号,我们可以看到Seq号和Ack号等一系列信息,它们用于网络包的排序、重传、流量控制等。虽然名曰“传输层”,但它并不是把网络包从一个设备传到另一个,而只是对传输行为进行控制。真正负责设备间传输的是下面两层。TCP是非常有用的协议,也是本书的重点。
📌 现在回想起来,如果当时老师能打开Wireshark,让我们看到这些实实在在的分层,我也不会困惑那么久了(假如那天我没有逃课的话)。
📌 历史上还真存在过这种情况—TCP和IP刚发明的时候就是合在一层的,后来才拆成两层
📌 因为网络对包的大小是有限制的,其最大值称为MTU,即“最大传输单元”。大多数网络的MTU是1500字节,但也有些网络启用了巨帧(Jumbo Frame),能达到9000字节。一个8192字节的包进入巨帧网络不会有问题,但到了1500字节的网络中就会被丢弃或者切分。被丢弃意味着传输彻底失败,因为重传的包还会再一次被丢弃。而被切分则意味着传输效率降低。
📌 由于这个原因,TCP不想简单地把8192 字节的数据一口气传给网络互连层,而是根据双方的MTU决定每次传多少。知道自己的MTU容易,但对方的MTU如何获得呢?如图5所示,在TCP连接建立(三次握手)时,双方都会把自己的MSS(Maximum Segment Size)告诉对方。MSS加上TCP头和IP头的长度,就得到MTU了。
📌 假如把客户端和服务器的 MTU 互换一下,这时客户端最大能发出多少字节的包呢?答案还是1500。因为无论接受方的MTU有多大,发送方都不能发出超过自己MTU的包。我们可以得到这样的结论:发包的大小是由MTU较小的一方决定的。
📌 在使用UDP的情况下,的确只用这两个包就完成了DNS查询。但在使用TCP时,要先用3个包(包号1、2、3)来建立连接。查询结束后,又用了4个包(包号7、8、9、10)来断开连接。Wireshark把这两种情况的差别完全显示出来了。我们可以从中看到连接的成本远远超过DNS查询本身,这对繁忙的DNS服务器来说无疑是巨大的压力。如果你的DNS还在使用TCP,该考虑更改了。
📌 发出的两个包Len=0,但其实是有TCP头的。头部本身携带的信息很多,所以不要以为Len=0就没意义。
📌 52号包的Seq=5129, Len=1448,所以来自接收方的53号包的Ack=5129+1448=6577,表示收到了6577之前的所有字节。理论上,接收方回复的Ack号恰好就等于发送方的下一个Seq号,
📌 · SYN:携带这个标志的包表示正在发起连接请求。因为连接是双向的,所以建立连接时,双方都要发一个SYN。
📌 · FIN:携带这个标志的包表示正在请求终止连接。因为连接是双向的,所以彻底关闭一个连接时,双方都要发一个FIN。
📌 实际环境中的 RST 往往意味着大问题。如果你在Wireshark中看到一个RST包,务必睁大眼睛好好检查。
📌 为什么要用三个包来建立连接呢,用两个不可以吗?其实也是可以的,但两个不够可靠。我们可以设想一个情况来说明这个问题:某个网络有多条路径,客户端请求建立连接的第一个包跑到一条延迟严重的路径上了,所以迟迟没有到达服务器。因此,客户端只能当作这个请求丢失了,不得不再请求一次。由于第二个请求走了正确的路径,所以很快完成工作并关闭了连接。对于客户端来说,事情似乎已经结束了。没想到它的第一个请求经过跋山涉水,还是到达了服务器。如图10所示,服务器并不知道这是一个旧的无效请求,所以按照惯例回复了
📌 假如TCP只要求两次握手,服务器上就这样建立了一个无效的连接。而在三次握手的机制下,客户端收到服务器的回复时,知道这个连接不是它想要的,所以就发一个拒绝包。服务器收到这个包后,也放弃这个连接。
📌 快递送货的策略非常浅显,几乎人人可以理解,而TCP传输大块数据的策略却很少人懂。事实上这两者的原理是相似的。
📌 TCP显然不用电瓶车送包,但它也有“往返”的需要。因为发包之后并不知道对方能否收到,要一直等到确认包到达,这样就花费了一个往返时间。假如每发一个包就停下来等确认,一个往返时间里就只能传一个包,这样的传输效率太低了。最快的方式应该是一口气把所有包发出去,然后一起确认。但现实中也存在一些限制:接收方的缓存(接收窗口)可能一下子接受不了这么多数据;网络的带宽也不一定足够大,一口气发太多会导致丢包事故。所以,发送方要知道接收方的接收窗口和网络这两个限制因素中哪一个更严格,然后在其限制范围内尽可能多发包。这个一口气能发送的数据量就是传说中的TCP发送窗口。
📌 不知道出于何种原因,TCP发送窗口的概念被广泛误解,比如,很多人会把接收窗口误认为发送窗口。我经常想在论坛上回答相关提问,却不知道该从何答起,因为有些提问本身就基于错误的概念。下面是一些经常出现的问题。
📌 这不是发送窗口,而是在向对方声明自己的接收窗口。在此例子中, 10.32.106.103向10.32.106.73声明自己的接收窗口是64093字节。10.32.106.73收到之后,就会把自己的发送窗口限制在64093字节之内
📌 假如接收方处理数据的速度跟不上接收数据的速度,缓存就会被占满,从而导致接收窗口为0。如图5的Wireshark截屏所示,89.0.0.85持续向89.0.0.210声明自己的接收窗口是win=0,所以 89.0.0.210的发送窗口就被限制为0,意味着那段时间发不出数据。
📌 图52.我如何在包里看出发送窗口的大小呢?很遗憾,没有简单的方法,有时候甚至完全没有办法。因为,当发送窗口是由接收窗口决定的时候,我们还可以通过“window size:”的值来判断。而当它由网络因素决定的时候,事情就会变得非常复杂(下篇文章将会详细介绍)。大多数时候,我们甚至不确定哪个因素在起作用,只能大概推理。以
📌 发送窗口和MSS有什么关系?发送窗口决定了一口气能发多少字节,而MSS决定了这些字节要分多少个包发完。举个例子,在发送窗口为16000字节的情况下,如果MSS是1000字节,那就需要发送16000/1000=16个包;而如果MSS等于8000,那要发送的包数就是16000/8000=2了。
📌 4.发送方在一个窗口里发出n个包,是不是就能收到n个确认包?不一定,确认包一般会少一些。由于TCP可以累积起来确认,所以当收到多个包的时候,只需要确认最后一个就可
📌 5.经常听说“TCP Window Scale”这个概念,它究竟和接收窗口有何关系?在TCP刚被发明的时候,全世界的网络带宽都很小,所以最大接收窗口被定义成 65535 字节。随着硬件的革命性进步,65535 字节已经成为性能瓶颈了,怎么样才能扩展呢?TCP 头中只给接收窗口值留了 16 bit,肯定是无法突破 65535 (216−1)的。
📌 1992年的RFC 1323中提出了一个解决方案,就是在三次握手时,把自己的Window Scale信息告知对方。由于Window Scale放在TCP头之外的Options中,所以不需要修改 TCP 头的设计。Window Scale 的作用是向对方声明一个 Shift count,我们把它作为2的指数,再乘以TCP头中定义的接收窗口,就得到真正的TCP接收窗口了。
📌 阅读本文之前,务必保证心情愉快,以免产生撕书的冲动;同时准备浓缩咖啡一杯,防止看到一半睡着了。因为这部分内容是TCP中最枯燥的,但也是最有价值的
📌 前文说到,发送方的发送窗口是受接收方的接收窗口和网络影响的,其中限制得更严的因素就起决定作用。接收窗口的影响方式非常简单,只要在包里用“Win=”告知发送方就可以了。而网络的影响方式非常复杂,所以留到本文专门介绍。
📌 难道就没有一个完美的方案吗?很遗憾,还真的没有。自网络诞生数十年以来,涌现过无数绝顶聪明的工程师,就是没有一个人能解决这个问题。幸好经过几代人的努力,总算有了一个最靠谱的策略。这个策略就是在发送方维护一个虚拟的拥塞窗口,并利用各种算法使它尽可能接近真实的拥塞点。网络对发送窗口的限制,就是通过拥塞窗口实现的。
📌 如果一口气发太多数据就可能遭遇拥塞,所以发送方把拥塞窗口的初始值定得很小。RFC 的建议是 2个、3个或者4个MSS,具体视MSS的大小而定。
📌 2.如果发出去的包都得到确认,表明还没有达到拥塞点,可以增大拥塞窗口。由于这个阶段发生拥塞的概率很低,所以增速应该快一些。RFC建议的算法是每收到n个确认,可以把拥塞窗口增加n个MSS。比如发了2个包之后收到2个确认,拥塞窗口就增大到2+2=4,接下来是4+4=8,8+8=16……这个过程的增速很快,但是由于基数低,传输速度还是比较慢的,所以被称为慢启动过程。
📌 3.慢启动过程持续一段时间后,拥塞窗口达到一个较大的值。这时候传输速度比较快,触碰拥塞点的概率也大了,所以不能继续采用翻倍的慢启动算法,而是要缓慢一点。RFC 建议的算法是在每个往返时间增加 1 个 MSS。比如发了 16 个MSS之后全部被确认了,拥塞窗口就增加到16+1=17个MSS,再接下去是17+1=18, 18+1=19……这个过程称为拥塞避免
📌 从慢启动过渡到拥塞避免的临界窗口值很有讲究。如果之前发生过拥塞,就把该拥塞点作为参考
📌 据。如果从来没有拥塞过就可以取相对较大的值,比如和最大接收窗口相等。
📌 无论是慢启动还是拥塞避免阶段,拥塞窗口都在逐渐增大,理论上一定时间之后总会碰到拥塞点的。那为什么我们平时感觉不到拥塞呢?原因有很多,如下所示。· 操作系统中对接收窗口的最大设定多年没有改动,比
📌 如 Windows 在不启用“TCP window scale option”的情况下,最大接收窗口只有64KB。而近年来网络有了长足进步,很多环境的拥塞点远在64KB以上。也就是说发送窗口已经被限制在64KB了,永远触碰不到拥塞点。
📌 很多应用场景是交互式的小数据,比如网络聊天,所以也不会有拥塞的可能。
📌 拥塞之后会发生什么情况呢?对发送方来说,就是发出去的包不像往常一样得到确认了。不过收不到确认也可能是网络延迟所致,所以发送方决定等待一小段时间后再判断。假如迟迟收不到,就认定包已经丢失,只能重传了。这个过程称为超时重传
📌 RTO。RTO的取值颇有讲究,理论上需要几个公式计算出来。根据多一道公式就会丢失一半读者的原理,本文将对此只字不提,我们只需要知道存在这么一段时间就可以了。有些操作系统上提供了调节RTO大小的参数。
📌 重传之后的拥塞窗口是否需要调整呢?非常有必要,为了不给刚发生拥塞的网络雪上加霜,RFC建议把拥塞窗口降到1个MSS,然后再次进入慢启动过程
📌 不难想象,超时重传对传输性能有严重影响。原因之一是在RTO阶段不能传数据,相当于浪费了一段时间;原因之二是拥塞窗口的急剧减小,相当于接下来传得慢多了。以我的个人经验,即便是万分之一的超时重传对性能的影响也非同小可
📌 当发送方收到3个或以上重复确认(Dup Ack)时,就意识到相应的包已经丢了,从而立即重传它。这个过程称为快速重传。之所以称为快速,是因为它不像超时重传一样需要等待一段时间
📌 为什么要规定凑满3个呢?这是因为网络包有时会乱序,乱序的包一样会触发重复的 Ack,但是为了乱序而重传没有必要
📌 没有拥塞时,发送窗口越大,性能越好。所以在带宽没有限制的条件下,应该尽量增大接收窗口,比如启用Scale Option(Windows上可参考KB 224829)。
📌 如果经常发生拥塞,那限制发送窗口反而能提高性能,因为即便万分之一的重传对性能的影响都很大
📌 很多操作系统上可以通过限制接收窗口的方法来减小发送窗口,Windows上同样可以参考KB224829。· 超时重传对性能影响最大,因为它有一段时间(RTO)没有传输任何数据,而且拥塞窗口会被设成1个MSS,所以要尽量避免超时重传。· 快速重传对性能影响小一些,因为它没有等待时间,而且拥塞窗口减小的幅度没那么大
📌 丢包对极小文件的影响比大文件严重。因为读写一个小文件需要的包数很少,所以丢包时往往凑不满3个Dup Ack,只能等待超时重传了。而大文件有较大可能触发快速重传。下面的实验显示了同样的丢包率对大小文件的不同影响:图11中的test是包含很多小文件的目录,而图12的hi是一个大文件。发生丢包时前者耗时增加了7倍多,而后者只增加了不到4倍
📌 假如把这个过程的包抓下来,会看到很多小包频繁来往于客户端和服务器之间。这种方式其实是很低效的,因为一个包的TCP头和IP头至少就40字节,而携带的数据却只有一个字符。这就像快递员开着大货车去送一个小包裹一样浪费。
📌 称为延迟确认。该策略的原理是这样的:如果收到一个包之后暂时没什么数据要发给对方,那就延迟一段时间(在Windows上默认为200毫秒)再确认。假如在这段时间里恰好有数据要发送,那确认信息和数据就可以在一个包里发出去了。第12号包就恰好符合这个策略,客户端收到11号包之后,等了41毫秒左右时我又输入一个字符。结果这个字符和对11号包的确认信息就一起装在12号包里了。延迟确认并没有直接提高性能,它只是减少了部分确认包,减轻了网络负担。有时候延迟确认反而会影响性能。微软的 KB 328890 提供了关闭延迟确认的步骤
📌 我一共输入了7个字符,但这些字符也被逐个打成小包了。能不能设计一个缓冲机制,把一个往返时间里生成的小数据收集起来,合并成一个大包呢?Nagle算法就实现了这个功能。这个算法的原理是:在发出去的数据还没有被确认之前,假如又有小数据生成,那就把小数据收集起来,凑满一个MSS或者等收到确认后再发送
📌 和延迟确认一样,Nagle 也没有直接提高性能,启用它的作用只是提高传输效率,减轻网络负担。在某些场合,比如和延迟确认一起使用时甚至会降低性能。微软也有篇KB指导如何关闭Nagle,但是一般没有这个必要,原因之一是很多软件已经默认关闭Nagle了。比如打开Putty,到“Connection”选项里可见“Disable Nagle’s algorithm”默认就是选中的
📌 现在人们说到 TCP/IP 时,指的已经不止是 TCP 和 IP 两个协议,而是包括了Application Layer、Transport Layer、Internet Layer和Network AccessLayer的四层模型。TCP处于Transport Layer,而IP处于Internet Layer。鲜为人知的是,一开始这两个协议并没有分层,而是合在一起的。
📌 TCP/IP的设计非常成功。30年来,底层的带宽、延时,还有介质都发生了翻天覆地的变化,顶层也多了不少应用,但TCP/IP却安如泰山。它不但战胜了国际标准化组织的OSI七层模型,而且目前还看不到被其他方案取代的可能。第一代从事TCP/IP工作的工程师,到了退休年龄也在做着朝阳产业。
📌 最近有了一些惊人的发现:我们都知道这个七层模型是由一个小组(见图3)完成的,但大家不知道的是,这个小组有一天深夜在酒吧里谈论美国的娱乐八卦。他们把迪斯尼电影里7个小矮人的名字写在餐巾纸上,有个人开玩笑说7对于网络分层是个好数字。第二天上午在标准化委员会的会议上,他们传阅了那张餐巾纸,然后一致同意昨晚喝醉时的重大发现。那天结束时,他们又给七个层次重新起了听上去更科学的名字,于是模型就诞生了。
📌 而当时业界普遍对待OSI模型的抵触态度,更是一个有力的佐证。幸好到了今天, OSI模型几乎名存实亡了,它对我们的影响只停留在还没来得及更新的教科书上。