调试解析直播弹幕消息protobuf内容,一步一步教你debug查看PushFrame和Response解码在哪里

我们知道直播间的弹幕消息是通过websocket传输的,而且传输的并不是明文数据,而是protobuf消息,至于为什么使用这个protobuf消息,因为它是二进制传输,更快更稳,相对于直播这种实时性比较高的要求,使用这种消息传输是非常合适的。

websocket连接

我们先要在web直播端看一下websocket的连接是在哪里建立的,至于怎么看这个websocket在哪里建立的,可以监听发送的ws请求,找到这个发送请求的代码位置:

监听消息

找到onMessage这个方法,这里面就是给这个socket实例添加了 this.socket.addEventListener("message", e) 方法,然后看一下这个e就是目标监听函数,这个函数在哪里呢?继续debug往下找:

再看看这个_receiveMessage函数里面是啥:

这里面还嵌套了一层es函数,这个es其实就是一个promise:

_receiveMessage里面就是处理收到消息的逻辑了,比较复杂,我们可以单独把它拿出来,然后添加上备注看一下大概都是什么意思。把代码拿出来,我们单独看一下里面的逻辑是啥:

会到debug状态,看一下这个e此时怎么像一个消息呢?没错,它就是一个消息:

再来看看t是啥?这怎么那么像弹幕或者聊天或者礼物或者观众的消息呢?是的,它就是:

每一个消息内容都有一个payload,里面就是真正的消息:

这里的ack消息里面就是需要使用PushFrame这个消息,里面添加payload_type + payload+LogID编码来的。

 

解析消息

上面的消息和payload内容都是二进制,怎么显示出来二进制的呢?

查看一下调用栈,发现这些消息都是送s里面导出来的,那这个s是从哪里来的?

s是这个 transport.decode(new Uint8Array(e.data)) 解析出来的:

那这个transport是啥,怎么解析的呢?找到了:

我们把代码折叠一下:这里就是创建了一个class e,其实这个e就是transport的类

看代码:

var g = f;.....而这个f就是下面的代码,也就是我截图的那个class e:
f = class e {constructor() {this.cachedType = {},this.loading = null,this.loadSchema = ()=>{"undefined" != typeof window && window.requestIdleCallback(()=>{this._loadSchema()})},this._loadSchema = ()=>(this.loading || (this.loading = (0,n.C)(this, null, function*() {if (u.roots.transport) {this.root = u.roots.transport,this.loading = Promise.resolve();return}yield(0,o.y)(),yield r.e(2986).then(r.bind(r, 69949)),this.root = u.roots.transport,this.loading = Promise.resolve()})),this.loading)}static get instance() {return e.__instance ? e.__instance : e.__instance = new e}static addRelation(t, r) {e.relation[t] = r,e.relation[r] = t}static setRelation(t) {e.relation = (0,n.i)((0,n.i)({}, e.relation), null != t ? t : {})}getType(t) {let r = t.replace(a.nl, ""), i = this.cachedType[r];if (i)return i;try {let i = [e.relation[t], e.relation[r], r, t].filter(e=>e), n = i.map(t=>e.typeHintPrefix.map(e=>`${e}.${t}`)).reduce((e,t)=>e.concat(t)).concat(i);d("search types", n);let o = n.reduce((e,t)=>e && "function" == typeof e ? e : t.split(".").reduce((e,t)=>null == e ? void 0 : e[t], this.root), void 0);if ("function" != typeof o)throw Error("cannot find type");return o} catch (e) {return d(`no current schema[${String(r)}]`),null}}// 这里就是transport的decode代码decode(e, t) {return (0,n.C)(this, null, function*() {var r, i, n, o, a, s, l, c, u;if (yield this._loadSchema(),t)return this._decode(e, t);let[p,h] = yield this._decodeFrameOrResponse(e), d = null != (o = null != (n = null == (i = null == (r = null == h ? void 0 : h.headers) ? void 0 : r.find(e=>"im-cursor" === e.key)) ? void 0 : i.value) ? n : p.cursor) ? o : "", m = null != (c = null != (l = null == (s = null == (a = null == h ? void 0 : h.headers) ? void 0 : a.find(e=>"im-internal_ext" === e.key)) ? void 0 : s.value) ? l : p.internal_ext) ? c : "";return {response: p,frame: h,needAck: null != (u = p.need_ack) && u,cursor: d,internalExt: m}})}encode(e, t) {return (0,n.C)(this, null, function*() {return yield this._loadSchema(),this._encode(e, t)})}ack(e, t) {return (0,n.C)(this, null, function*() {var r, i, n, o;let l = null != (o = null != (n = null == (i = null == (r = e.headers) ? void 0 : r.find(e=>"im-internal_ext" === e.key)) ? void 0 : i.value) ? n : t.internal_ext) ? o : "";return yield this.encode({payload_type: a.AG.Ack,payload: s(l),LogID: e.LogID}, "PushFrame")})}ping() {return this.encode({payload_type: a.AG.Hb}, "PushFrame")}_decodeFrameOrResponse(e) {return (0,n.C)(this, null, function*() {try {let t = this._decode(e, "PushFrame"), r = yield this._extractResponse(t);return [this._decode(r, "Response"), t]} catch (t) {return [this._decode(e, "Response")]}})}_extractResponse(t) {return (0,n.C)(this, null, function*() {var r;return (null == (r = t.headers) ? void 0 : r.some(e=>"compress_type" === e.key && "gzip" === e.value)) ? yield e.unGzip(t.payload) : t.payload})}_decode(e, t) {let r = this.getType(t);if (!r)return;let i = r.decode(e);return d("decoded success", t, i),m("decoded success", e),i}_encode(e, t) {let r = this.getType(t);if (!r)return;let i = r.encode(e).finish();return d("encoded success", t, e),m("encoded success", i),i}}

那这个decode里面怎么解析数据的?看代码,找到_decode和_decodeFrameOrResponse这两个函数的内容,看到了什么?原来解码的就在这里啊:

到这里我觉得你应该就知道怎么解析了吧,下课

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

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

相关文章

spring-boot-starter-data-redis是否支持reactive响应式编程

开源项目SDK:https://github.com/mingyang66/spring-parent 个人文档:https://mingyang66.github.io/raccoon-docs/#/ spring-boot-starter-data-redis: 使用传统的基于阻塞的I/O编程模型,这意味着当你调用Redis操作时&#xff0…

【24考研·交通】我的考研经历

文章目录 一、考前准备二、政治备考三、英语一备考四、数学一备考五、运筹学备考六、复试/调剂七、结语 距离24考研上考场过去快半年了,距离我拟录取也两个月多了,现在回想起来,最大的感受是:好像做了一场大梦。 其实这篇文章在考…

PLL输出频率之间为什么会相互影响

这个问题触及了PLL(锁相环)设计的核心挑战之一。让我们逐步分析这个问题: PLL的基本结构: PLL通常包含一个压控振荡器(VCO)、分频器、鉴相器和环路滤波器。VCO产生一个高频时钟,然后通过不同的分…

Java异常处理详解【高级篇】

Java异常处理详解【高级篇】 Java异常处理详解【高级篇】1. 自定义异常类2. 异常处理最佳实践3. 异常链与异常传播4. 异常处理与并发编程5. 资源管理与异常6. 异常处理框架Java异常处理详解【高级篇】 异常处理在Java编程中扮演着至关重要的角色,特别是在大型应用程序或系统开…

SQL-DDL操作

数据库操作 登录MySQL PS D:\WorkSpace\MachineLearning\DL_learning> mysql -u root -p Enter password: ****** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 12 Server version: 8.0.37 MySQL Community Server - GPLCopy…

61、Flink 的 Histogram 累加器代码示例

1、概述 Histogram 是 key-value 累加器。 2、代码示例 import org.apache.flink.api.common.JobExecutionResult; import org.apache.flink.api.common.accumulators.Histogram; import org.apache.flink.configuration.Configuration; import org.apache.flink.streaming.…

Profibus协议转profinet协议网关模块连接电机保护器与PLC通讯

一、背景 工业通讯中常见的协议有:Modbus协议,ModbusTCP协议,Profinet协议,Profibus协议,Profibus DP协议,EtherCAT协议,EtherNET协议等在现代工业控制系统中具有重要的角色。而Profibus协议转…

Horror病毒原理和解析【附靶场+网安学习视频】

Windows XP Horror Edition:这是一种伪装成Windows XP更新的应用程序,实际上会安装恶意软件。一旦运行,它会显示一个假更新,完成后屏幕会显示各种恐怖效果和文字。这个程序会更改桌面背景、图标和系统设置,甚至尝试重写…

不能创建第三个变量,实现两个数的交换

目录 常规实现两个数的交换(如:交换变量a和变量b) 方法一:加减法 方法二:异或操作符 常规实现两个数的交换(如:交换变量a和变量b) 创建一个临时变量tmp,先将其中一个…

matlab 计算导数

边界提取 一、算法原理1、主要函数2、参考文献二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、主要函数 Y = diff(X)计算沿大小不等于 1 的第一个数组维度的 X X…

STM32——使用TIM输出比较产生PWM波形控制舵机转角

一、输出比较简介: 只有高级定时器和通用寄存器才有输入捕获/输出比较电路,他们有四个CCR(捕获/比较寄存器),共用一个CNT(计数器),而输出比较功能是用来输出PWM波形的。 红圈部分…

python内网上传下载工具

python内网上传下载工具 利用python的nicegui写了一个内网下载工具,支持上传和下载。 这样的工具很多,但是利用这个模块可以用很少的代码做一个比较好看的页面。 上传的文件会存在当前目录下生成一个upload文件夹中;下载则是读取upload文件夹…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷9(公有云)

#需要资源(软件包及镜像)或有问题的,可私聊博主!!! #需要资源(软件包及镜像)或有问题的,可私聊博主!!! #需要资源(软件包…

Stable Diffusion【真人模型】:人像光影摄影极限写实真实感大模型

大家好,我是极客菌 今天和大家分享一个基于SD1.5的真人大模型:人像光影摄影极限写实真实感大模型。 该模型具有以下特点: 真实肤感(在面部肌理和皮肤肌理上均有加强学习,拒绝ai出图假的问题) 永不脱妆&a…

5年工作经验面试经验以及面试题分享

第一家面试题 评价 全是八股文 面试题 MySQL索引类型 索引结构 联合索引可以设置索引类型 不同索引性能差异巨大 基础索引有哪些 B Tree索引和Hash索引 Redis基本数据结构 List是原子的吗 原子性和可见性区别是什么 MySQL的存储过程和视图 MySQL性能优化有哪些 MySQL的存储…

数据结构7---图

一、定义 对于图的定义,我们需要明确几个注意的地方:一线性表中我们把数据元素叫元素,树中叫结点,在途中数据元素我们则称之为顶点(Vertex)。 对于图的定义,我们需要明确几个注意的地方: 线性表中我们把数据元素叫元素&#xf…

使用shell脚本编写监控系统资源(CPU,内存,磁盘)使用情况

🏡作者主页:点击! 🛠️Shell编程专栏:点击! ⏰️创作时间:2024年6月20日16点30分 🀄️文章质量:95分 目录 ————前言———— 1.本章目标 2.编写脚本 1.获取内…

Java异常处理详解【入门篇】

Java异常处理详解【入门篇】 Java异常处理详解1. 异常的概念2. 异常的分类2.1 检查异常(Checked Exception)2.2 非检查异常(Unchecked Exception)2.3 错误(Error) 3. 异常处理机制3.1 try-catch3.2 finally…

CesiumJS【Basic】- #025 生成kml/kmz文件

文章目录 生成kml/kmz文件1 目标2 代码2.1 main.ts生成kml/kmz文件 1 目标 生成kml/kmz文件(kmz是kml的压缩格式) 2 代码 首先添加点线面并且赋予不同的颜色,然后导出生成kml/kmz文件 2.1 main.ts import * as Cesium from "cesium";const viewer = new Ces…

微服务 | Springboot整合GateWay+Nacos实现动态路由

1、简介 路由转发 执行过滤器链。 ​ 网关,旨在为微服务架构提供一种简单有效的统一的API路由管理方式。同时,基于Filter链的方式提供了网关的基本功能,比如:鉴权、流量控制、熔断、路径重写、黑白名单、日志监控等。 基本功能…