Files
JKVideo/docs/bilibili-live-danmaku-research.md
Developer e4760b1f07 1
2026-03-17 19:52:26 +08:00

6.5 KiB
Raw Blame History

B站直播弹幕连接 — 学习笔记

参考项目:https://github.com/Minteea/bilibili-live-danmaku (v0.7.15)


一、完整连接流程

1. 初始化 Cookie
   GET  https://www.bilibili.com/               → 种植初始 cookie
   POST .../bilibili.api.ticket.v1.Ticket/GenWebTicket → 获取 bili_ticket + WBI 签名密钥
   GET  .../x/frontend/finger/spi               → 获取 buvid4
   POST .../x/internal/gaia-gateway/ExClimbWuzhi → 激活 buvid模拟指纹

2. 获取弹幕服务器信息(需要 WBI 签名)
   GET https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id={roomId}
   响应:{ token, host_list: [{ host, wss_port, ws_port }] }

3. 建立 WebSocket 连接
   wss://{host_list[0].host}/sub
   fallback: wss://broadcastlv.chat.bilibili.com/sub

4. onopen → 发送 AUTH 包op=7
5. 收到 op=8 (CONNECT_SUCCESS) → 立即发送一次心跳op=2
6. 每次收到 op=3 (HEARTBEAT_REPLY) → 30秒后发下一次心跳
7. 收到 op=5 (MESSAGE) → 解析弹幕数据

二、二进制包格式16字节包头

┌──────────┬──────────┬──────────┬──────────┬──────────┐
│  03     │  45     │  67     │  811    │  1215   │
│ 包总长度  │ 头部长度  │ 协议版本  │ 操作码   │ 序列号   │
│ uint32   │ uint16   │ uint16   │ uint32   │ uint32   │
│ (BE)     │ 固定=16  │ ver      │ op       │ 固定=1   │
└──────────┴──────────┴──────────┴──────────┴──────────┘
│ 16+ bytes: body (JSON 字符串 或 压缩数据)              │
└────────────────────────────────────────────────────────┘

操作码 (op)

op 名称 方向 说明
2 HEARTBEAT 客→服 心跳包body 为空
3 HEARTBEAT_REPLY 服→客 心跳回应body 是 uint32 在线人数
5 MESSAGE 服→客 弹幕/通知消息
7 USER_AUTHENTICATION 客→服 进房认证包
8 CONNECT_SUCCESS 服→客 认证成功

协议版本 (ver)

ver 说明 解压方式
0 原始 JSON 直接 JSON.parse
1 uint32 DataView.getUint32(0)
2 zlib 压缩包束 pako.inflate() → 递归解包
3 brotli 压缩包束 BrotliDecode() → 递归解包

ver=2/3 解压后包含多个子包拼接,需按每个包头的 totalLen 逐个切割。


三、认证包 (op=7) 的 body 字段

{
  "uid": 0,
  "roomid": 12345678,
  "protover": 3,
  "platform": "web",
  "type": 2,
  "key": "<token from getDanmuInfo>",
  "buvid": "<buvid3 cookie>"
}
字段 必填 说明
uid 未登录填 0
roomid 真实房间号(非短号)
protover 推荐 3brotli或 2zlib
platform 固定 "web"
type 固定 2
key getDanmuInfo 返回的 token
buvid 推荐 buvid3 cookie 值,不填可能被限流

四、心跳逻辑

onopen
  └─ 发送 AUTH (op=7)

收到 op=8 (CONNECT_SUCCESS)
  └─ 立即发送第一次心跳 (op=2, body='')

收到 op=3 (HEARTBEAT_REPLY)
  ├─ 清除上次计时器
  └─ 30秒后发送下次心跳 (op=2)

重点:心跳是在收到 op=3 后才重排计时,而不是固定 setInterval。 这样即使因网络延迟导致心跳回复慢,也不会提前发第二次心跳。


五、getDanmuInfo API

GET https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo
    ?id={roomId}
    &w_rid=xxx   ← WBI 签名参数
    &wts=xxx

响应:

{
  "code": 0,
  "data": {
    "token": "L_DSj3cX...",
    "host_list": [
      { "host": "tx-gz-live-comet.chat.bilibili.com", "wss_port": 443, "ws_port": 2244 },
      { "host": "broadcastlv.chat.bilibili.com", "wss_port": 443, "ws_port": 2244 }
    ]
  }
}

也可使用旧版 getConf(无需 WBI 签名,但已被标记为 deprecated

GET https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id={roomId}&platform=h5

六、与我们现有实现的差异

对比项 我们现在 参考项目 建议
API getConf(老接口) getDanmuInfo(新接口) 可选,两个都能用
WBI 签名 有(之前因 -352 移除) 如果遇到 -352 可用 getConf
protover 2zlib 3brotli或 2 先用 2brotli 需 polyfill
auth 字段 缺少 platformbuvid platform: "web"buvid 需补充
心跳方式 setInterval 固定 30s 收到 op=3 后计时 30s 建议改为 op=3 驱动
buvid3 未传入 auth 作为 buvid 字段传入 auth 需补充

七、我们代码需要的改进

1. 补充 auth 字段

const authBody = JSON.stringify({
  uid: 0,
  roomid: roomId,
  protover: 2,
  platform: 'web',    // ← 缺少
  type: 2,
  key: token,
  buvid: buvid3,      // ← 缺少(从 AsyncStorage 读取)
});

2. 心跳改为 op=3 驱动

// 收到 op=8 → 立即发第一次心跳
if (pkt.op === 8) {
  sendHeartbeat();
}
// 收到 op=3 → 30秒后再发
if (pkt.op === 3) {
  clearTimeout(heartbeatTimer);
  heartbeatTimer = setTimeout(sendHeartbeat, 30000);
}

3. ver=3 (brotli) 解压支持

如果 protover 设为 3需要引入 brotli 解码库React Native 需要 polyfill。 建议暂时保持 protover=2zlibpako 已支持。


八、弹幕消息 cmd 类型

op=5 的 body 是 JSONcmd 字段标识消息类型:

cmd 说明
DANMU_MSG 弹幕消息,info[1] 是文字
SEND_GIFT 送礼物
GUARD_BUY 上舰
SUPER_CHAT_MESSAGE SC 醒目留言
INTERACT_WORD 进入直播间/关注
WATCHED_CHANGE 观看人数变化
ONLINE_RANK_COUNT 在线排名人数
ROOM_CHANGE 直播间信息变更
LIVE 开播
PREPARING 下播

九、DANMU_MSG info 数组结构

info[0][2]  弹幕颜色(十进制 RGB
info[1]     弹幕文字内容
info[2][0]  用户 uid
info[2][1]  用户名
info[2][2]  是否房管1=是)
info[3][0]  粉丝勋章等级
info[3][1]  粉丝勋章名
info[7]     舰长等级0无1总督2提督3舰长