调试解析直播弹幕消息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这两个函数的内容,看到了什么?原来解码的就在这里啊:

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

抖音protobuf是一种协议格式,用于在抖音应用中传输和解析数据。它使用了protobufProtocol Buffers)作为序列化和反序列化的工具。protobuf是一种轻量级、高效的数据交换格式,可以将结构化数据序列化成二进制格式,以便于传输和存储。 抖音使用protobuf来定义和传输各种类型的数据,包括用户信息、视频信息、评论等等。通过protobuf抖音可以更高效地传输和解析这些数据,提高应用的性能和效率。 关于具体的抖音protobuf字段,根据引用的描述,抖音protobuf协议字段主要类型都被解析了,但还有一些没有被处理的字段。具体字段的含义和用途可能因抖音的更新而有所变化。 如果你对抖音protobuf协议感兴趣,你可以参考引用和引用中提到的相关材料和步骤,了解如何使用protobuf解析抖音的数据,并对其进行验证和实践。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [抖音protobuf协议字段.主要类型都解析了.剩下没多大用的没去处理.基于网页版对应解析](https://download.csdn.net/download/loveljsheng/85077727)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [抖音web端私信protobuf 初识](https://blog.csdn.net/qq_25541217/article/details/126699725)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [解析抖音直播protocbuf弹幕数据,礼物、评论、关注、来了](https://blog.csdn.net/didadidadada/article/details/119836515)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1024小神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值