调试解析直播弹幕消息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,一经查实,立即删除!

相关文章

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波形的。 红圈部分…

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

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

数据结构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…

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

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

在python docker中安装ESL库

概述 功能需求,把python脚本移植到docker中。 因为python脚本中有使用freeswitch的ESL接口,所以需要安装python-ESL依赖库。 本文记录在python:3.10.14-slim的docker镜像上编译安装python-ESL依赖库的流程。 环境 docker engine: Version 24.0.6 d…

【ES】--Elasticsearch的翻页详解

目录 一、前言二、from+size浅分页1、from+size导致深度分页问题三、scroll深分页1、scroll原理2、scroll可以返回总计数量四、search_after深分页1、search_after避免深度分页问题一、前言 ES的分页常见的主要有三种方式:from+size浅分页、scroll深分页、search_after分页。…

基于Java微信小程序同城家政服务系统设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟感兴趣的可以先收藏起来,还…

【Sublime】Sublime Text 中运行终端

Sublime Text 本身并不是一个终端仿真器,可以使用插件来在 Sublime Text 中集成终端功能。最常用的插件之一是“Terminal”。 使用“Terminal”插件在 Sublime Text 中启动终端 以下是安装和使用该插件的步骤: 安装 Package Control: 如果你…

面试突击:HashMap 源码详解

本文已收录于:https://github.com/danmuking/all-in-one(持续更新) 数据结构 JDK1.8 之前 JDK1.8 之前 HashMap 采用 数组和链表 结合的数据结构。如下图: HashMap 将 key 的 hashCode 经过扰动函数处理过后得到 hash 值&#…

java第二十九课 —— 断点 | 零钱通项目

断点调试(debug) 实际需求 在开发中,新手程序员在查找错误时,这时老程序员就会温馨提示,可以用断点调试步一步的看源码执行的过程,从而发现错误所在。 重要提示:在断点调试过程中,…

Open3D(C++) 删除点云中重复的点

目录 一、算法原理1、重叠点2、主要函数二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、重叠点 原始点云克隆一份   构造重叠区域   合并点云获得重叠点 2、主要…

嫦娥六号平安回家,Smartbi非常荣幸参与中国航天项目

“小时不识月,呼作白玉盘。”李白的这句诗,承载了古人对月亮的美好想象与纯真童趣。今天,当我们仰望夜空,那轮明月不仅是诗词中的意象,更是科学探索的目标和梦想的寄托。 2024年6月25日14时07分,嫦娥六号返…

vxeTable反转表格

文章目录 前言 前言 如果遇到列为动态值&#xff0c;行相对固定的情况&#xff0c;这种时候就需要用到行列反转&#xff0c;这里我以vxeTable表格为例。 直接上代码 <vxe-gridref"tableRefRight":auto-resize"true":columns"dataColumn":dat…

第5章_Modbus通讯协议

文章目录 5.1 学习Modbus的快速方法5.1.1 寄存器速记5.1.2 协议速记 5.2 初识Modbus5.2.1 背景5.2.2 什么是Modbus&#xff1f;1. Modbus简介2. Modbus特点3. Modbus常用术语4. Modbus事务处理 5.3 Modbus软件与使用5.3.1 Modbus软件简介5.3.2 Modbus Poll&#xff08;主站设备…