拿下奇怪的前端报错:1比特丢失导致的音视频播放时长无限增长-浅析http分片传输核心和一个坑点

news2024/12/30 3:10:33

问题背景

在一个使用MongoDB GridFS实现文件存储和分片读取的项目中,同事遇到了一个令人困惑的问题:音频文件总是丢失最后几秒,视频文件也出现类似情况。更奇怪的是,播放器显示的总时长为无限大。这个问题困扰了团队成员几天,都没有解决,因为架构这块我负责的,最后当然就需要我来深入调查了(本来是想蒙混过去的,毕竟影响其实不大,但因为刚好有点空闲)。

  • 在应用中,音频时长为无限,可以不停的快进也就是时长无限增长,大部分手机都如此,除了一部分手机正常
  • 在浏览器中单独打开音频文件,播放正常
  • 文件使用Mongodb的GridFS存储在数据库,通过http接口对外提供读取接口

HTTP分片传输简介

在深入问题之前,让我们先了解一下HTTP分片传输的基本概念,这对理解后续的调试过程至关重要。这部分内容很久之前看网络相关文档的时候,有初略的了解,大概就是前端请求时,后端支持分片可返回部分数据+总数据长度,然后前端就可以按需读取了。

HTTP分片传输(HTTP Range Requests)允许客户端请求资源的部分内容,而不是整个资源。这在处理大文件,特别是音频和视频流时非常有用。主要涉及以下HTTP头:

  1. Range:客户端在请求中使用此头来指定所需的字节范围。
    例如:Range: bytes=500-999表示请求从第500字节到第999字节的内容。

  2. Content-Range:服务器在响应中使用此头来指明实际返回的字节范围。
    例如:Content-Range: bytes 500-999/1234表示返回的是500-999字节,总文件大小为1234字节。

  3. Accept-Ranges:服务器使用此头来告诉客户端它支持范围请求。

重要的是,HTTP规范中定义的Range是包含性的(inclusive),这意味着Range: bytes=0-499实际上请求了500个字节。这里是一个非常容易出错的点,因为在大多数的编程语言中,对类似数组数据的操作,结尾位置的数据都是不包含的,这也是一个坑点,刚入行不久的小伙伴很可能就跳进去了,毕竟随能够记得那么细呢?而且是一个反常见模式的知识点!

调试过程

1. 初步分析

首先,我注意到播放器的duration显示为无穷大。为了进一步测试,我尝试将currentTime设置为一个非常大的值,但问题依然存在。但是在单独的页面播放和oppo的某手机播放没问题,就猜测是某个特殊点的兼容性问题,但部分尝试做了兼容,所以正常,但大部分的设备或者webview都没有兼容,导致这个问题没被处理,从而出现时长无限。这时候测试得到一个比较重要的信息:

  • 特别小的文件,时长在5秒内的不会出现无限时长,视频文件大概率都会出现

于是就进一步猜测,肯定和网络传输有关,而且和文件体积有关系,这时候想起了大文件分片,因为上传的时候就是分片上传的

2. 网络请求分析

观察到网络请求一直处于pending状态,我怀疑可能是由于socket没有正确关闭导致无限加载。我尝试在数据流关闭后结束请求,但这并没有解决问题。

  1. 而且发现浏览器在不同的发送包内容基本一样的请求

3. 请求头分析

仔细查看请求头,我发现了一些奇怪的Range请求,如Range=11333-11333/11334。起初,我认为可能是因为起始值和结束值相同导致的问题。我尝试在这种情况下重写Range,对于Range=11333-这样的请求则返回416状态码。但这些措施都没有解决问题。

也就是这里引起了我的警觉,因为是不停的在请求,一开始的时候,还以为是触发了某个浏览器的bug,但是分析请求头,有一点引起可我的注意,就是11333-11333/11334 ,为什么会请求这样的数据,第一反应是一个0字节的请求

然后我就开始读HTTP1.1-rfc2616规范文档,发现这了这个反直觉的坑,那就是范围的结尾值是包含的。

这里基本上我猜测到了问题出现的原因,下一步就是验证了

附rfc的说明 - inclusive,开始还不确定这个单词的意思
在这里插入图片描述

4. 深入源码

分析第三方库的源码后,我发现了问题的根源:

  • HTTP的Content-Range头中,start和end都是包含的(inclusive)。
  • 而在JavaScript和MongoDB中,类似slice()这样的操作,end是不包含的(exclusive)。

这种不一致性导致了在处理最后一个字节时出现问题。具体来说:

  1. 当客户端请求最后一个字节时(例如Range: bytes=11333-11333),服务器正确地解释了这个请求。
  2. 但是,当服务器使用JavaScript或MongoDB的API来获取这个范围的数据时,由于end是exclusive的,实际上没有返回任何数据。
  3. 这导致客户端认为还有更多数据需要获取,因此会继续发送请求,造成无限加载的情况。

解决方案

为了解决这个问题,便对getDownloadStream方法进行了修改-重写,类似于下面的方式-先缓存老方法,再修改请求参数,再调用老方法:

const oldFun = obj.a;
obj.a = function(a1, a2) {
  if (a2.end) {
    a2.end += 1;
  }
  return oldFun(a1, a2);
}

这个简单的修改确保了JavaScript/MongoDB的操作范围与HTTP请求的Range一致,解决了缺少最后一个字节的问题。通过将end值增加1,确保了包含了请求范围的最后一个字节。

经验总结

  1. HTTP协议的深入理解很重要:尤其是请求头参数的精确含义,对于解决复杂问题至关重要。在这个案例中,理解Range请求的包含性本质是解决问题的关键。

  2. 框架设计的重要性:这次调试过程让我意识到了Koa等框架设计的优势。能够在一个中间件中同时处理请求和响应,大大方便了调试过程。

  3. 细节决定成败:在这个案例中,仅仅一个字节的差异就导致了严重的用户体验问题。这提醒我们在处理底层数据时必须格外小心,特别是在处理不同系统间的数据交换时。

  4. 开源贡献的价值:通过这次经历,我不仅解决了自己的问题,还有机会通过PR为开源社区做出贡献,提升了项目的整体质量。这展示了开源社区如何通过集体智慧来解决复杂问题。

  5. 跨系统集成的挑战:这个问题突出了在集成不同系统(如HTTP服务器、JavaScript运行时和MongoDB)时可能遇到的细微差异。在设计跨系统解决方案时,需要特别注意这些潜在的不一致性。

结语

这个看似简单的问题背后隐藏着HTTP协议、JavaScript特性和数据库操作之间的微妙差异。通过耐心的调试和深入的分析,我不仅解决了问题,还加深了对各种技术细节的理解。这再次证明,在软件开发中,“魔鬼藏在细节里”。

对于其他可能遇到类似问题的开发者,建议深入理解所使用的每个组件的特性,特别是在处理底层数据传输时。同时,这个案例也强调了全面测试的重要性,尤其是在处理边界情况时。通过分享这种经验,可以共同提高整个开发社区的技术水平和问题解决能力。

本文主要是复盘之前工作中遇到的一个奇怪的前端问题,并且是我个人的第一次开源代码贡献,还提交了两次,第一次因为格式不对没写注释-个人仓库管理员没接受,第二次说明了情况并详细说明和备注了解释等才通过

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

gradle build --offline idea怎么配置 打包命令使用gradle build --offline进行打包怎么操作

两种方式 1:一种命令行执行gradle build --offline 2:一种直接gradle这里单击离线

多模态理论——什么是多模态?多模态的关键技术?

多模态理论 文章目录 多模态理论1.什么是多模态(multimodal)2.深度学习中的多模态3.多模态学习的关键技术3.1模态表示3.2多模态对齐3.3多模态融合 4.多模态任务 1.什么是多模态(multimodal) 模态指的是数据或者信息的表现形式&am…

完美收官丨飞易通端到端整体解决方案助力嵌入式技术发展

2024年10月10日,Embedded World 2024展会在美国德克萨斯州奥斯汀完美收官。Embedded World 2024是嵌入式系统领域的全球性盛会,汇聚了来自世界各地的专业人士,促进了嵌入式系统、工业自动化、汽车技术和通信系统领域的合作与发展。 应展会举办…

深入解析 Go 语言中的结构体:从基础用法到高级技巧的全方位指南

结构体(Struct)是 Go 语言中的一种重要数据类型,能够帮助我们将多个数据组合成一个自定义的类型。与其他编程语言的类(Class)类似,结构体允许我们定义字段、方法,并灵活操作数据。本文将从基础到…

遥控器传输信号算法详解!

一、算法概述 无人机遥控器信号传输算法主要基于无线通信技术,通过特定的调制、编码和信号处理技术,将遥控器的操作指令转化为无线电信号,并传输给无人机。无人机接收到信号后,再将其解码为可识别的指令,从而实现对无…

10月造价、注安考试的押题资料,到底有没有用?

​随着考试的临近,各大考试机构也开始推出了各种考前押题资料。面对网络铺卷而来的考前押题资料,考生自己也无从判断资料的质量,那该如何选择靠谱的押密资料呢?今天考试100就带大家来分析一下: 1、正确看待押题资料 首…

标签页(tabs)内容的显示和样式

1. 代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Tabs Example</title> &…

吴恩达深度学习笔记(一)-基础知识

上图三种神经网络&#xff1a;标准、卷积&#xff08;用于图像&#xff09;、循环&#xff08;序列化数据、时序数据&#xff09; 数据类型&#xff1a;结构化&#xff08;表格&#xff09;、非结构化&#xff08;语言、图片、文本&#xff09; 二分类问题&#xff1a; 0、1判…

Qt-窗口对话框QFileDialog的使用(53)

目录 描述 相关函数 使用 描述 文件对话框 ⽂件对话框⽤于应⽤程序中需要打开⼀个外部⽂件或需要将当前内容存储到指定的外部⽂件 相关函数 打开⽂件&#xff08;⼀次只能打开⼀个⽂件&#xff09; QString getOpenFileName 打开多个⽂件&#xff08;⼀次可以打开多个⽂件…

2024下半年软考中级网络工程师,这100题,必做!

第一章节&#xff1a;计算机硬件基础&#xff08;5&#xff09; 1、在机器指令的地址字段中&#xff0c;直接指出操作数本身的寻址方式称为&#xff08; &#xff09;。 A 隐含寻址 B 寄存器寻址 C 立即寻址 D 直接寻址 答案&#xff1a;C 解析: 立即寻址方式通常直接在指…

Copilot Coaching新功能铸就Word更强

Copilot 的意思是副驾驶。 现在&#xff0c;您的副驾驶教练来了&#xff1a;Copilot Coaching Copilot Coaching 是 Word 中的一项新 Copilot 功能&#xff0c;可在您查看内容时为您提供支持&#xff0c;以实现语法和拼写之外的改进 - 帮助您澄清想法&#xff0c;并为您提供有…

Granafa配置基于elasticsearch数据源的折线图

Granafa可以直接配置基于elasticsearch数据源的折线图&#xff0c;用于展示es库中数据的最值、均值、总量等

AI绘画教程 重阳节海报设计

本期 AI 绘画的主题是海报设计&#xff0c;让我们一同探索能设计出哪些不同风格的海报吧。 今天是重阳节&#xff0c;一起用 AI 绘画制作一些画风清新的海报吧。正所谓 “遥知兄弟登高处&#xff0c;遍插茱萸少一人”。让我们一起通过这些设计&#xff0c;在对长辈的思念与祝福…

2022年全国大学生数学建模竞赛E题目-小批量物料生产安排详解+思路+Python代码时序预测模型

E题补完计划开始,接着之前专栏里面的E题内容写完接下来的所有问题。经过第一问得到了最佳物料编码的前六位排名: 是通过聚合指标和熵权法计算得到的,现在我们需要完成第一问的接下来两个目标,分别是建立物料需求的周预测模型和利用历史数据对预测模型进行评价。对于周预测…

Python物联网编程:10个IoT设备通信的脚本

今天我们要聊的是如何使用Python编写脚本来实现10个IoT设备之间的通信。物联网&#xff08;IoT&#xff09;是一个充满无限可能的领域&#xff0c;它将日常设备连接到互联网&#xff0c;使它们能够互相通信、收集数据并做出响应。Python以其简洁易懂的语法和强大的库支持&#…

力扣题/回溯/N 皇后

N 皇后 力扣原题 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解…

代码随想录day32:动态规划part5

完全背包 二维 /* 完全背包&#xff1a;动态规划 */ int unboundedKnapsackDP(int[] wgt, int[] val, int cap) {int n wgt.length;// 初始化 dp 表int[][] dp new int[n 1][cap 1];// 状态转移for (int i 1; i < n; i) {for (int c 1; c < cap; c) {if (wgt[i -…

009——二叉树

目录 二叉树的五种基本形态&#xff1a; 1.二叉树可以是空树 2.只有一个根节点的树 3.斜树&#xff1a;只有左子树或右子树的树 4.左右孩子都有的树 二叉树的性质&#xff1a; 1.假设根节点是第一层&#xff0c;在二叉树的第i层上最多有2^(n-1)个结点 2.深度为k的二叉树…

【JAVA+flowable】工作流 获取流程节点 几种方法总结

flowable中 获取流程中任务节点的方法有好几种 1.这种是常见的一种 获取流程 正在激活 中任务节点 List<HistoricActivityInstance> historicActivityInstanceList historyService.createHistoricActivityInstanceQuery().processInstanceId(procInsId).activityTyp…

Java面试宝典-Java集合02

目录 Java面试宝典-Java集合02 21、TreeMap 和 TreeSet 在排序时如何比较元素&#xff1f; 22、ArrayList 和 LinkedList 的区别是什么&#xff1f; 23、ArrayList 和 Vector 的区别&#xff1f; 24、队列和栈是什么&#xff1f;有什么区别&#xff1f; 25、Queue和Deque的区别…