序
B 站直播流协议整体上比较简单,只是收集命令比较耗费精力。本文将介绍 B 站 WebSocket 协议使用了的包体和结构定义。
通讯过程
准备连接
获取自身 UID (可选)
略。获取方法太多,最常见的为
https://api.bilibili.com/x/web-interface/nav
,带 Cookie
访问即可。
获取真实房间号
连接前需要调取
https://api.live.bilibili.com/room/v1/Room/room_init
,以获得真实房间号。
最小示例请求,无需鉴权:
curl -G 'https://api.live.bilibili.com/room/v1/Room/room_init?id=7777' --compressed
返回示例:
{
"code": 0, // 状态码, 0 为成功
"msg": "ok", // 错误信息
"message": "ok", // 错误信息
"data": { // 数据
"room_id": 545068, // 真实房间号
"short_id": 7777, // 短房间号, 可能为 0, 代表无
"uid": 8739477, // 主播 UID
"need_p2p": 0,
"is_hidden": false,
"is_locked": false,
"is_portrait": false,
"live_status": 1, // 暂停 = 0, 直播 = 1, 轮播 = 2
"hidden_till": 0,
"lock_till": 0,
"encrypted": false,
"pwd_verified": false,
"live_time": 1677038397,
"room_shield": 0,
"is_sp": 0,
"special_type": 0 // 普通 = 0, 付费 = 1, 拜年纪 = 2
}
}
获取 WSS 服务器及密钥
访问
https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo
可获取消息流服务器及认证包所需的密钥。
最小示例请求,无需鉴权,id
应为真实房间号,否则可能连接到错误的直播间:
curl 'https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=545068' --compressed
返回示例:
{
"code": 0,
"message": "0",
"ttl": 1,
"data": {
"group": "live", // <\
"business_id": 0, // <
"refresh_row_factor": 0.125, // < 这五个参数没有多大意义
"refresh_rate": 100, // <
"max_delay": 5000, // </
"token": "nDI********I0ll-ryrd-", // 密钥, 认证包的时候会用到
"host_list": [
{
"host": "live.chat.bilibili.com", // 域名
"port": 2243, // 端口
"wss_port": 2245, // WSS 端口
"ws_port": 2244 // WS 端口
}
// 剩下为其他分流, 略
]
}
}
域名应任选一 $.data.host_list[]
中的,拼接为
wss://$host:$wss_port/sub
。
WebSoket 消息流包体结构
见图:
其中 Header
的 Protocol
和
Type
常数为:
enum Protocol {
= 0,
Command = 1,
Special = 2,
CommandZlib = 3,
CommandBrotli }
enum Type {
= 2,
Heartbeat = 3,
HeartbeatResp = 5,
Command = 7,
Certificate = 8,
CertificateResp }
Sequence
参数可一直为
1
,HeadSize
恒为
16
,TotalSize
即为 16 + 包体长度。
客户端仅可能发 Special
协议,且为 Heartbeat
或 Certificate
类型的包。
建立连接
使用 WSS 协议向之前获取到的域名建立连接。所有通讯都使用 WebSocket 的 Binary Frame。
在连接后,应在几秒之内首先发送 Certificate
包,否则服务器会中断连接。
包头应为 Special
协议和 Certificate
类型。
JSON 包体定义为:
struct Certificate {
: u64, // 此处 UID 或许可以填 0, B 站游客可看到弹幕流
uid : u64,
roomid : String,
key : u8,
protover }
其中 key
为之前获取到的密钥,roomid
为要连接的房间号,protover
可选值为:
enum Protover {
= 1,
Normal = 2,
Zlib = 3,
Brotli }
该值影响了之后命令会收到的压缩类型。无压缩协议似乎已被禁用,只可使用 Zlib 及 Brotli 压缩协议。
此时,服务器会返回 CertificateResp
类型的包,其 JSON
定义为 { code: number }
,code
为 0
即代表鉴权成功。
此时服务器也会开始发送 Command
类型的业务包。
客户端还需要每隔一段时间向服务器发送心跳包,以保持连接。心跳包头为
Special
协议和 Heartbeat
类型。包体可为
[Object object]
字符串。
服务器在收到心跳包后,会返回 HeartbeatResp
。包体或许为
UInt32
的人气值。不过该属性可能已被弃用,往往为
1
。
Command
类型存在几种不同的压缩方式,需要通过包头的
Protocol
来判断包体如何读取。
当 Protocol
为 Command
时,包体为单个
JSON
,直接解码即可。
当 Protocol
为 CommandZlib
或
CommandBrotli
时,包体会嵌套一层连续的包(参见上文配图)。需要先解压包体后读取。即嵌套的包头和包体均被压缩。
业务包 JSON
结构一般为:
{
"cmd": "CMD_ID",
"data": {
"payload": "payload1",
}
}
不过有时也会在顶层有其他属性,如 DANMU_MSG
:
{
"cmd": "DANMU_MSG",
"dm_v2": "略",
"info": [ /* 略 */ ]
}
业务包解析
见 Plutus。