調試解析直播彈幕消息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.pswp.cn/diannao/37626.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/37626.shtml
英文地址,請注明出處:http://en.pswp.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鏈的方式提供了網關的基本功能,比如:鑒權、流量控制、熔斷、路徑重寫、黑白名單、日志監控等。 基本功能…