iPad在Tailscale上:一个WebRTC调试故事
如果您不熟悉p2claw的工作方式,值得在深入这个主题之前先查看一下相关的博客文章。我在我的iPad上打开了一个p2claw应用,但显示的是空白页面。相同的URL在我的Mac、Linux电脑和手机上都能正常工作。它们在同一个WiFi、相同的浏览器引擎以及同一网络下。就像一个好的侦探故事一样,我们提出了一系列嫌疑人 [iPad,然后是WebKit,再然后是Tailscale],结果它们都证明是无辜的。可以这么说,真正的问题是两个“穿着大衣”的bug:webrtc-rs中的一个硬编码常量,以及我们通过顽固的努力发现的Tailscale的一项设计决策。我们在同一天找到了一个解决方案,但理解我们实际修补了什么花了两周时间。投诉是应用加载了足够的HTML以显示加载状态,然后就挂起了。没有相关的控制台错误,服务工作线程已注册,WebRTC握手完成,数据通道已打开 [dc.readyState === "open"],然后就什么都没有了。浏览器通过数据通道发送了第一个GET /请求,却无止境地等待响应。另一端的盒子代理认为一切正常。它已经提供了响应并将字节推送到通道中。但这些字节永远没有到达iPad。事情并没有那么简单,实际上这是一个海森堡bug:如果我疯狂刷新,页面有时会加载。当怀疑时,进行监测。我们所做的第一件有用的事情是记录连接的两个端点,并按时钟时间对齐日志:盒子发送的每个数据块,浏览器接收到的每个数据块,尤其是盒子在等待确认交付时所持有的出站缓冲区中的数据量。这帮助我们找到了数据没有到达另一端的位置。死胡同。在丢弃了所有信息,直到包括WebRTC握手之后,我们感到茫然。我们检查了一些WebRTC特定的限制并再次确认网络稳定性。消息大小限制。在WebRTC中,在两个对等体开始交换数据之前,它们会就每个对等体接受的最大单个块大小达成一致。如果您发送的内容超过该大小,一些浏览器会默默地挂起。我们从两台设备上读取了该限制(maxMessageSize)。iPad报告为64kb,正好与Mac相同,远远高于我们发送的7-8kb块。在这之后,我们觉得可以排除消息块大小作为罪魁祸首,这使得真正的诊断更加困难。断断续续的WiFi。最简单的解释:数据包在传输过程中丢失。使用ifstat和tcpdump在盒子上数据是正常的,而我的手机 [在同一WiFi上] 并没有出现同样的问题。必须是iPad上独有的某种东西,但我们没有头绪。数字实际上所说的内容。每个请求,盒子发送了三个块:一个220字节的头部,一个7,874字节的主体,以及一个199字节的尾部。我们新的监测显示发送方的出站缓冲区上升到约8kb并停止。它持有它“发送”的主体,但无法确认它已到达。当iPad刷新时,我们看到了完全相同的模式。WebRTC数据通道在损失的UDP上保证有序交付,因此一个丢失的块会阻塞随后的消息。在iPad上,在浏览器的JS控制台中,我们看到只有一个块被接收 [220字节的头部],然后就什么都没有了。我们没有看到主体或后续请求的小头部。我们在Mac上的Safari上进行了测试,猜测问题可能出在WebKit上,因为在每个iOS浏览器上都发生了这个问题 [而所有iOS浏览器在底层都是WebKit] ,但Mac却顺利接收了8kb和11kb的块,没有任何问题。 "是Tailscale造成的"。在经历了两小时的WebKit理论讨论后,我意识到,与Mac不同,iPad启用了Tailscale。Tailscale是一个VPN,VPN将流量包装在一个额外的层中,使每个数据包的空间减少。因此,相比于Mac,大响应在到达iPad时被切割成了更多、更小的部分。WebKit自己在用户空间中实现了数据通道,包括从承载它们的数据包中重新组装大消息。我们的理论朝着WebKit消息重新组装中的一个bug发展。我们将盒子的消息限制在800字节,足够小以便每个消息都能通过单个数据包传输,iPad立即加载,无论Tailscale是否开启。感觉就像是案子解决了 [实际上第一次尝试1200字节的消息,Claude帮我计算应该能适应,但神秘地没有成功。记住这一点]。事后看来,我们才发现问题在于VPN,但我们仍然坚守自己的WebKit理论。考虑到我们上下文的膨胀 [包括我和代理的上下文,毕竟这是在AI时代进行故障排除],我们发现Tailscale的问题被吸收到WebKit理论中,而不是对它提出挑战。如果我们审视网络和WebRTC发送器,但我们却将其视为浏览器有问题的又一个理由。因此,我们将事件写成iOS Safari的bug [设备获取到数据包但从不将其重新组装给应用],并开始构建一个独立的复现以证明这一点。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡