
之前发过一篇如何屏蔽B站直播P2P上传的文章,在评论区陆陆续续看到有一些网友提出了困惑,加上B站一直在不断微调P2P相关js的代码,导致原专栏内容和现有代码有出入。借此机会发一篇更新的文章,同时解答一些常见的问题。
关于P2P技术本身和为什么要屏蔽P2P,这里不再用长篇大论展开解说。简单概括就是:
当你打开B站直播的时候,服务器会给你的设备源源不断地推送直播流量(推流)。
与此同时,B站会把你的设备当作服务器,给其他正在观看同一直播的用户反向推流。
你开的画质越高,反向推流的画质往往也越高,占用上传带宽越大。
同一直播间人数越多,你被P2P的概率越大。你可能正在被其他用户的设备推流,也可能给其他设备推流,甚至可能在同时进行。
官方并不提供关闭此功能的方法。
结果就是:
大量占用你的上行带宽和计算资源,造成设备发烫、卡顿和额外的网络占用。
P2P网络质量往往较差,容易造成直播观看卡顿。
但推流的任务本应该是B站的服务器,而不是普通用户的设备去做的。
那么如何屏蔽这个P2P功能?
目前我个人的思路主要有两种,一是通过屏蔽/修改/阻断P2P功能所需要的接口,从而让P2P无法正常工作、报错退出,二是通过网络拦截功能阻断对关键域名的请求流量,使P2P组件无法与中间服务器进行通信,从而失效。下面简单分享两种方法的研究思路和实现方法。
打开一个观看人数较多的直播间,F12开发人员工具转到控制台,往上滚动到最开始,发现这样一些信息:

从字面意思推断,Misaka和Last Order(魔禁和超炮中的角色)应该是P2P相关功能组件的代号。第三行:
Misaka Network Check: Supported
这很可能是在检测当前网络是否支持P2P。常理分析,如果此处检测结果为false,P2P组件很可能会停止工作。点击右侧链接进行跳转,发现对应的函数名为checkP2PSupport:

确实改了,还增加了更详细的判断机制。我记得之前只有Supported和Unsupported。
核心判断逻辑在这一行:
null !== this.p2pImpl && (e = !0 === this.p2pImpl.SistersPlayerContext.checkP2PSupport() ? a.P2PSupportCheckResult.Supported : !0 === this.p2pImpl.SistersPlayerContext.checkP2PSupportBypass() ? a.P2PSupportCheckResult.Blocked : a.P2PSupportCheckResult.BrowserNotSupported);
很有趣的代码书写方式。如果一时看不明白,可以自行做一下代码简化。判断t为Supported的逻辑是:this.p2pImpl不为null且checkP2PSupport()为true。this.p2plmpl的逻辑我没有去研究,感觉大概率不为null。那么只需要重点关注checkP2PSupport即可。跟踪函数定义:

其实就是在判断P2P所需的各个关键接口和函数是否可用。若任意一种(不是一个)接口不可用,则判定Unsupported。那么只需要delete掉其中任意一种接口就可以达到目的。例如以下三行:
delete window.RTCPeerConnection;
delete window.mozRTCPeerConnection;
delete window.webkitRTCPeerConnection;
挂到油猴上即可。记得把运行时期调得靠前一些,比如document-start。刷新后效果如下:

到此为止就成功了。
这种方法不如第一种来得彻底,原因是它并不屏蔽P2P的组件或功能,P2P仍然可以继续运行。B站给P2P加入了重连机制,每10s重试一次,因此该方法会让P2P陷入无限的重连中。对于不支持油猴的浏览器,此方法仍然适用。
首先打开一个人数较多的直播间,F12开发人员工具。放通所有流量,观察网络活动。由于推流是持续进行的,因此我们截取后几秒观察流量。

一堆fetch是服务器向自己的推流,属于正常的流量。还有两个ws,一个是sub,尝试拦截后发现弹幕消失,由此判断是弹幕的ws。还有一个ws,不知道是什么。详情看一下:

看到一堆peer字样,感觉很像是在进行P2P配对。观察一下域名:

域名中的tracker字样显得更加可疑。虽然写的是tracker,让人联想到跟踪、收集使用情况,但详细信息中的一堆peer让人觉得没这么简单。再看看发起程序:

有P2P的字样。嫌疑很大。尝试屏蔽一下。
反复刷新页面,多开几个直播间,发现相关域名格式相似,长这样:
cn-sdqd-ccc-live-tracker-02.网页链接
cn-sdqd-ccc-live-tracker-01.网页链接
在adblock中添加以下规则进行匹配:
*tracker*.网页链接

刷新页面。观察控制台。发现此时P2P组件反复尝试连接但失败。任务管理器中,上传流量恢复正常。由此判断已经成功。

根据此思路,只要将此类域名的正则表达式输入路由器的拦截规则,就可以屏蔽局域网内所有B站P2P流量。如果不支持正则表达式,进行穷举再批量添加也不失为一种可行的方案。
由此又想到一个问题,即这些格式相同的域名是如何下发的?
找到创建相关ws的请求,跟踪函数,打断点,刷新。发现服务器的URL存在一个名为p2pContext.Config.trackerServers的数组中,猜测是通过fetch或xhr获取的。

回到网络面板,搜索tracker,果然找到一个xhr请求

果然是这个。

顺手把URL核心部分扔进adblock,刷新。结果发现还能连接上,奇怪了。继续打断点调试刷新,发现此时只有一个域名,这个域名是死的,猜测可能是写死在文件中的。

全文搜索,发现出处。果然是写死了。

adblock规则再+1。刷新,发现已经彻底阻断了连接。

美中不足的是,xhr获取URL列表的域名网页链接过于通用,不能直接屏蔽此域名,而所有请求均是https协议,路由器无法通过URL匹配进行屏蔽,因此使用正则表达式匹配*tracker*.网页链接仍是最佳选择。
到此over。
回答几个uu的问题:
Q:为什么按照之前的文章添加了adblock规则后,看不到弹幕了?
A:B站更新了弹幕websocket的URL,现在是 wss://zj-cn-live-comet.网页链接:2245/sub 这样的格式,相比之前显式地指定了端口号,导致白名单规则没有匹配到。
Q:其他平台的p2p怎么屏蔽呢?
A:不同平台的思路大致相同,但实现肯定有一些差别。如果有其他想要屏蔽p2p的平台,可以留言评论哦~有空会研究的!
有帮助的话不妨点个赞吧~