Chrome DevTools可以直接查看binary类型数据包了!

B站直播ws协议分析

连接

通过Chrome DevTools可以看到,网页版B站直播的弹幕通过 wss://tx-sh3-live-comet-04.chat.bilibili.com/sub 传输,这个地址每次是不一样的。需要通过 https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=房间号&platform=pc&player=web 获得wss地址 (应该是负载均衡的需要,实际测试连接任意wss地址都能正常获取弹幕) 。

封包头部16个字节用于标识数据包的长度及类型,数据包格式详见下表。字节序均为大端模式。参考:https://blog.csdn.net/xfgryujk/article/details/80306776

偏移长度类型含义
04int数据包长度
42int数据包头部长度,固定为 16
62int数据包协议版本(见下文)
84int数据包类型 (见下文)
124int固定为 1
16-byte[]数据主体
数据包协议版本含义
0数据包有效负载为未压缩的JSON格式数据
1客户端心跳包,或服务器心跳回应(带有人气值)
2数据包有效负载为通过zlib压缩后的JSON格式数据
数据包类型发送方名称含义
2客户端心跳不发送心跳包,50-60秒后服务器会强制断开连接
3服务器心跳回应有效负载为直播间人气值
5服务器通知有效负载为礼物、弹幕、公告等
7客户端认证(加入房间)客户端成功建立连接后发送的第一个数据包(见下文)
8服务器认证成功回应服务器接受认证包后回应的第一个数据包

客户端建立连接后,需要在5秒内发出加入房间(认证)的数据包,否则会被服务器强制断开连接。其中有效负载的key字段内容可以从之前的 https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=房间号&platform=pc&player=web 获取。如发送的认证包格式错误,服务器会立刻强制断开连接,JSON字段的详细说明见下表。

字段类型必选含义
uidnumber×用户UID
roomidnumber房间号
protovernumber×协议版本,目前为 2
platformstring×平台,可以是 web
clientverstring×客户端版本,可以是 "1.8.5"
typenumber×未知,可以是 2
keystring×用户标识,通过之前提到的接口获取

心跳

心跳包建议每30秒发一次,头部16字节遵守上文规则,负载内容随意。(b站通过传入一个空对象来生成心跳包,然后就有了我之前无法理解的心跳包内容)

通知(弹幕、公告、礼物等)

有新的弹幕、礼物或其他公告时,服务器会发送类似下图的数据包,首先需要使用 zlib.inflate 解压数据主体部分(除头部16字节外)。解压后的数据头部十六字节与原数据头相同,去除头部后即为JSON格式数据

这里我们以一个弹幕数据包为例子

原始弹幕数据包
解压后

可以看到解压后的数据依然带有头部(16字节),去除头部后即为JSON格式数据,其中的cmd字段更为详细地描述了数据包类型。其中比较明显的是,['info'][1] 代表弹幕内容, ['info'][2][1] 为发送者,['info'][9]['ts'] 为发送时间戳,cmd的已知格式参考下表

cmd字段含义
DANMU_MSG收到弹幕
SEND_GIFT有人送礼
WELCOME欢迎加入房间
WELCOME_GUARD欢迎房管加入房间
SYS_MSG系统消息
PREPARING主播准备中
LIVE直播开始

下面我展示了几个常见cmd的数据包JSON格式,还有很多活动通知cmd,各位可以自行抓取(如下面的 ACTIVITY_BANNER_UPDATE_V2 就是我写文章当天出现的活动通知cmd类型)

{
  "cmd": "SEND_GIFT",
  "data": {
    "giftName": "辣条",
    "num": 5,
    "uname": "迪多玛索",
    "face": "http://i2.hdslb.com/bfs/face/1a3b795aafc5887f3f33909c7e66876d23911979.jpg",
    "guard_level": 0,
    "rcost": 42593386,
    "uid": 415822879,
    "top_list": [],
    "timestamp": 1570368091,
    "giftId": 1,
    "giftType": 0,
    "action": "喂食",
    "super": 0,
    "super_gift_num": 0,
    "price": 100,
    "rnd": "EF27025C-4C20-440F-B36F-64CCFABBF68E",
    "newMedal": 0,
    "newTitle": 0,
    "medal": [],
    "title": "",
    "beatId": "",
    "biz_source": "live",
    "metadata": "",
    "remain": 0,
    "gold": 0,
    "silver": 0,
    "eventScore": 0,
    "eventNum": 0,
    "smalltv_msg": [],
    "specialGift": null,
    "notice_msg": [],
    "capsule": null,
    "addFollow": 0,
    "effect_block": 1,
    "coin_type": "silver",
    "total_coin": 500,
    "effect": 0,
    "broadcast_id": 0,
    "draw": 0,
    "crit_prob": 0,
    "tag_image": "",
    "user_count": 0,
    "send_master": null
  }
}
{
  "cmd": "ACTIVITY_BANNER_UPDATE_V2",
  "data": {
    "id": 378,
    "title": "第6名",
    "cover": "",
    "background": "https://i0.hdslb.com/bfs/activity-plat/static/20190904/b5e210ef68e55c042f407870de28894b/14vZu7h9pK.png",
    "jump_url": "https://live.bilibili.com/p/html/live-app-rankcurrent/index.html?is_live_half_webview=1&hybrid_half_ui=1,5,85p,70p,FFE293,0,30,100,10;2,2,320,100p,FFE293,0,30,100,0;4,2,320,100p,FFE293,0,30,100,0;6,5,65p,60p,FFE293,0,30,100,10;5,5,55p,60p,FFE293,0,30,100,10;3,5,85p,70p,FFE293,0,30,100,10;7,5,65p,60p,FFE293,0,30,100,10;&anchor_uid=22550271&is_new_rank_container=1&area_v2_id=163&area_v2_parent_id=3&rank_type=master_realtime_area_hour&area_hour=1",
    "title_color": "#8B5817",
    "closeable": 1,
    "banner_type": 4,
    "weight": 18,
    "add_banner": 0
  }
}
{
  "cmd": "ROOM_REAL_TIME_MESSAGE_UPDATE",
  "data": {
    "roomid": 101526,
    "fans": 294665,
    "red_notice": -1
  }
}

后记:Chrome支持直接显示WebSocket二进制包真的能节省很多时间。吹爆 Chrome!


在迷失中寻找自我