B站与斗鱼直播弹幕WS分析
前言
最近在看直播时萌生了个想法,想看看直播的弹幕能否爬取下来,于是结合几篇文章,研究了一下两个平台的直播弹幕协议。
B站弹幕
B站直播的弹幕发送采用的是WS协议,连接地址wss://broadcastlv.chat.bilibili.com:2245/sub
在建立连接后需要在5s内发送认证包加入房间,否则服务器会断开连接。认证包结构见下。
完成认证后,需要每60s发送一次心跳包进行心跳维持,否则服务器会断开连接。心跳包结构见下。
B站使用的数据包格式为:
偏移 | 长度 | 类型 | 含义 |
---|---|---|---|
0 | 4 | int | 数据包长度 |
4 | 2 | int | 数据包头部长度,固定值为16 |
6 | 2 | int | 数据包协议版本 |
8 | 4 | int | 包类型 |
12 | 4 | int | 作用不详,固定为1 |
16 | - | byte[] | 数据包内容 |
数据包协议版本 | 含义 |
---|---|
0 | 负载为未压缩的JSON数据 |
1 | 心跳包,或者心跳回复(房间人气值) |
2 | 使用zlib压缩 |
3 | 使用brotli压缩 |
数据包类型 | 发送方 | 名称 | 含义 |
---|---|---|---|
2 | 客户端 | 心跳 | 维持心跳,60s后未收到心跳包,服务端会断开连接 |
3 | 服务器 | 心跳回应 | 负载为房间人气值 |
5 | 服务器 | 通知 | 负载为弹幕、礼物、公告等内容 |
7 | 客户端 | 认证 | 建立连接后发送的第一个数据包,用以加入房间 |
8 | 服务器 | 认证回应 | 服务器接受认证包后的回应数据包 |
认证包有效载荷:
字段 | 类型 | 必须 | 含义 |
---|---|---|---|
uid | int | 是 | 用户ID,可以为0 |
roomid | int | 是 | 房间ID |
protover | int | 否 | 协议版本,目前为2 |
platform | string | 否 | 平台,可以为web |
type | int | 否 | 类型,目前为2 |
key | string | 否 | 密钥,可以为空 |
心跳包:
偏移 | 长度 | 类型 | 含义 | 值 |
---|---|---|---|---|
0 | 4 | int | 数据包长度 | 16 |
4 | 2 | int | 数据包头部长度 | 16 |
6 | 2 | int | 数据包协议版本 | 1 |
8 | 4 | int | 包类型,2为心跳包 | 2 |
12 | 4 | int | 作用不详,固定为1 | 1 |
16 | - | byte[] | 数据包内容 | 无 |
B站直播链接中的id并不等于room_id,真实的room_id可以通过请求https://api.live.bilibili.com/room/v1/Room/room_init?id={id}
获取。
当返回的数据包协议版本为2或3时,数据包的内容部分解压后也是一个或多个嵌套数据包,去除数据包头部后才为JSON原始数据。
解包后的数据包主体部分部分字段及其内容:
字段 | 含义 |
---|---|
cmd | 数据包类型,如DANMU_MSG、SEND_GIFT等 |
[“info”][1] | 弹幕内容 |
[“info”][2][0] | 弹幕发送者UID |
[“info”][2][1] | 弹幕发送者昵称 |
[‘info’][9][‘ts’] | 发送时间戳 |
cmd | 含义 |
---|---|
DANMU_MSG | 弹幕消息 |
SEND_GIFT | 礼物消息 |
WELCOME | 欢迎消息 |
WELCOME_GUARD | 欢迎房管消息 |
SYS_MSG | 系统消息 |
PREPARING | 准备中 |
LIVE | 直播开始 |
斗鱼弹幕
斗鱼的官方文档写的很全面,这里直接贴官方文档
斗鱼开放平台文档
1. 弹幕服务简介
为了避免弹幕获取流量浪费,提高弹幕分发效率,我们提供了以tcp协议为基准的弹幕消息推送服务,整体流程如下:
- 连接弹幕服务器;
- 登陆鉴权;
- 进入房间弹幕频道;
- 接受弹幕消息(保持心跳);
- 断开弹幕连接;
2. 弹幕协议
2.1 协议组成
众所周知,受 TCP 最大传输单元(MTU)限制及连包机制影响,应用层协议需自己设计协议头,以保证不同消息的隔离性和消息完整性。斗鱼后台协议头设计如下:
斗鱼消息协议格式如上所示,其中字段说明如下:
消息长度:4 字节小端整数,表示整条消息(包括自身)长度(字节数)。
消息长度出现两遍,二者相同。
消息类型:2 字节小端整数,表示消息类型。取值如下:
689 客户端发送给弹幕服务器的文本格式数据
690 弹幕服务器发送给客户端的文本格式数据。
加密字段:暂时未用,默认为 0。保留字段:暂时未用,默认为 0。
数据部分:斗鱼独创序列化文本数据,结尾必须为‘\0’。详细序列化、反序列化算法见下节。(所有协议内容均为 UTF-8 编码)
2.2 序列化
为增强兼容性、可读性斗鱼后台通讯协议采用文本形式的明文数据。同时针对本平台数据特点,斗鱼自创序列化、反序列化算法。即 STT 序列化。下面详细介绍 STT 序列化和反序列化。STT 序列化支持键值对类型、数组类型。规定如下:
- 键 key 和值 value 直接采用‘@=’分割
- 数组采用‘/’分割
- 如果 key 或者 value 中含有字符‘/’,则使用‘@S’转义
- 如果 key 或者 value 中含有字符‘@’,使用‘@A’转义举例:
(1) 多个键值对数据:key1@=value1/key2@=value2/key3@=value3/
(2) 数组数据:value1/value2/value3/ 不同消息有相同的协议头、序列化方式。下面将对上文中提到的重要消息内
容的序列化文本进行说明。2.3 客户端消息格式
客户端向弹幕服务器发送消息时,头部消息类型字段为 689。2.3.1 登录请求消息
该消息用于完成登录授权,以aid为yihanTest为例,完整的数据部分应包含的字段如下:
type@=loginreq/roomid@=58839/aid@=yihanTest/token@=4c8421535f9639d8c1ad35d1fa421f36/time@=1574850339/auth@=45619bb990e6b76db06a66d5a8a446d7/
字段名 | 字段说明 |
---|---|
type | 表示为“登陆请求”消息,固定为 loginreq |
roomid | 所登录房间的 ID |
aid | 开发平台身份标识ID |
token | 通过 http://openapi.douyu.com/api/thirdPart/token 获取的身份凭证 |
time | 当前时间戳 |
auth | 鉴权参数 |
Auth 生成方式为 md5({secret}_{aid}_{time}_{token}), secret为aid对应的秘钥
2.3.2 客户端旧版心跳消息
该消息用于维持与后台间的心跳(旧版),完整的数据部分应包含的字段如下:
type@=keeplive/tick@=1439802131/
字段名 | 字段说明 |
---|---|
type | 表示为“心跳”消息,固定为 keeplive |
tick | 当前 unix 时间戳 |
2.3.3 客户端新版心跳消息
该消息用于维持与后台间的心跳(新版),完整的数据部分应包含的字段如下:
type@=mrkl/
字段名 | 字段说明 |
---|---|
type | 表示为“心跳”消息,固定为 mrkl |
2.3.4 入组消息
该消息用于完成加入房间分组,完整的数据部分应包含的字段如下:
type@=joingroup/rid@=59872/aid@=yourapplicaitonID/token@=4c8421535f9639d8c1ad35d1fa421f36/time@=1574850339/auth@=xxxxxxxxxxxx/
字段名 | 字段说明 |
---|---|
type | 表示为“加入房间分组”消息,固定为 joingroup |
aid | 开发平台身份标识ID |
rid | 所登录的房间号 |
token | 获取方式见https://open.douyu.com/source/api/8 |
time | 当前时间戳 |
auth | 鉴权参数 |
Auth 生成方式为 md5({secret}_{aid}_{time}_{token}), secret为aid对应的秘钥
2.3.5 订阅贵族排行变动
该消息用于订阅贵族排行变动消息,完整的数据部分应包含的字段如下:
type@=sub/mt@=online_vip_list/
字段名 | 字段说明 |
---|---|
type | 表示为“订阅”消息,固定为 sub |
mt | 消息类型,暂仅支持online_vip_list |
2.3.6 登出消息
该消息用于完成登出后台服务,完整的数据部分应包含的字段如下:
type@=logout/
字段名 | 字段说明 |
---|---|
字段名 | 字段说明 |
type | 表示为“登出”消息,固定为 logout |
2.4 服务端消息格式
服务端向客户端发送消息时,头部消息类型字段为 690。
2.4.1 登录响应消息
服务端返回登陆响应消息,完整的数据部分应包含的字段如下:
type@=loginresp/msg@=ok/rid@=77614265/
字段名 | 字段说明 |
---|---|
type | 表示为“登录”消息,固定为 loginresp |
msg | 登录响应 |
rid | 房间ID |
2.4.2 弹幕消息
用户在房间接收弹幕时,服务端发送此消息给客户端,完整的数据部分应包含的字段如下:
type@=chatmsg/rid@=58839/ct@=8/hashid@=9LA18ePx4dqW/nn@=test/txt@=666/cid@=1111/ic@=icon/sahf@=0/level@=1/nl@=0/nc@=0/cmt@=0/gt@=0/col@=0/rg@=0/pg@=0/dlv@=0/dc@=0/bdlv@=0/gatin@=0/ chtin@=0/repin@=0/bnn@=test/bl@=0/brid@=58839/hc@=0/ol@=0/rev@=0/hl@=0/ifs@=0/p2p@=0/el@=eid@AA=1@ASetp@AA=1@ASsc@AA=1@AS/
申请权限时请选择“普通弹幕消息”。
字段名 | 字段说明 |
---|---|
type | 表示为“弹幕”消息,固定为 chatmsg |
rid | 房间 id |
hashid | 发送者 uid |
nn | 发送者昵称 |
txt | 弹幕文本内容 |
level | 用户等级 |
gt | 礼物头衔:默认值 0(表示没有头衔) |
col | 颜色:默认值 0(表示默认颜色弹幕) |
ct | 客户端类型:默认值 0 |
rg | 房间权限组:默认值 1(表示普通权限用户) |
pg | 平台权限组:默认值 1(表示普通权限用户) |
dlv | 酬勤等级:默认值 0(表示没有酬勤) |
dc | 酬勤数量:默认值 0(表示没有酬勤数量) |
bdlv | 最高酬勤等级:默认值 0(表示全站都没有酬勤) |
cmt | 弹幕具体类型: 默认值 0(普通弹幕) |
sahf | 扩展字段,一般不使用,可忽略 |
ic | 用户头像 |
nl | 贵族等级 |
nc | 贵族弹幕标识,0-非贵族弹幕,1-贵族弹幕,默认值 0 |
gatin | 进入网关服务时间戳 |
chtin | 进入房间服务时间戳 |
repin | 进入发送服务时间戳 |
bnn | 徽章昵称 |
bl | 徽章等级 |
brid | 徽章房间 id |
hc | 徽章信息校验码 |
ol | 主播等级 |
rev | 是否反向弹幕标记: 0-普通弹幕,1-反向弹幕, 默认值 0 |
hl | 是否高亮弹幕标记: 0-普通,1-高亮, 默认值 0 |
ifs | 是否粉丝弹幕标记: 0-非粉丝弹幕,1-粉丝弹幕, 默认值 0 |
p2p | 服务功能字段 |
el | 用户获得的连击特效:数组类型,数组包含 eid(特效 id),etp(特效类型),sc(特效次数)信息,ef(特效标志)。 |
2.4.3 礼物消息
用户在房间赠送礼物时,服务端发送此消息给客户端。完整的数据部分应包含的字段如下:
type@=dgb/gfid@=1/gs@=59872/gfcnt@=1/hashid@=1/rid@=1/nn@=someone/level@=1/dw@=1/
申请权限时请选择“赠送礼物消息”。
字段名 | 字段说明 |
---|---|
type | 表示为“赠送礼物”消息,固定为 dgb |
rid | 房间 ID |
gid | 弹幕分组 ID |
gfid | 礼物 id |
gs | 礼物显示样式 |
hashid | 用户 id |
nn | 用户昵称 |
bg | 大礼物标识:默认值为 0(表示是小礼物) |
ic | 用户头像 |
eid | 礼物关联的特效 id |
level | 用户等级 |
dw | 主播体重 |
gfcnt | 礼物个数:默认值 1(表示 1 个礼物) |
hits | 礼物连击次数:默认值 1(表示 1 连击) |
dlv | 酬勤头衔:默认值 0(表示没有酬勤) |
dc | 酬勤个数:默认值 0(表示没有酬勤数量) |
bdl | 全站最高酬勤等级:默认值 0(表示全站都没有酬勤) |
rg | 房间身份组:默认值 1(表示普通权限用户) |
pg | 平台身份组:默认值 1(表示普通权限用户) |
nl | 贵族等级:默认值 0(表示不是贵族) |
sahf | 扩展字段,一般不使用,可忽略 |
bnn | 徽章昵称 |
bl | 徽章等级 |
brid | 徽章房间 id |
hc | 徽章信息校验码 |
fc | 攻击道具的攻击力 |
2.4.4 用户进房通知消息
具有特殊属性的用户进入直播间时,服务端发送此消息至客户端。完整的数据部分应包含的字段如下:
type@=uenter/rid@=1/ uid@=1/nn@=someone/str@=1/l evel@=1/el@=eid@AA=1@ASetp@AA=1@ASsc@AA=1@AS@S/
字段名 | 字段说明 |
---|---|
type | 表示为“用户进房通知”消息,固定为 uenter |
rid | 房间 ID |
uid | 用户 ID |
nn | 用户昵称 |
str | 战斗力 |
level | 新用户等级 |
gt | 礼物头衔:默认值 0(表示没有头衔) |
rg | 房间权限组:默认值 1(表示普通权限用户) |
pg | 平台身份组:默认值 1(表示普通权限用户) |
dlv | 酬勤等级:默认值 0(表示没有酬勤) |
dc | 酬勤数量:默认值 0(表示没有酬勤数量) |
bdlv | 最高酬勤等级:默认值 0 |
ic | 用户头像 |
nl | 贵族等级 |
ceid | 扩展功能字段 id |
crw | 用户栏目上周排名 |
ol | 主播等级 |
el | 用户获得的连击特效:数组类型,数组包含 eid(特效 id),etp(特效类型), ,sc(特效次数)信息,ef(特效标志) |
wgei 页游欢迎特效 id
2.4.5 房间开关播提醒
房间开播提醒主要部分应包含的字段如下:
type@=rss/rid@=1/ss@=1/code@=1/rt@=0/notify@=1/endtime@=1/
字段名 | 字段说明 |
---|---|
type | 表示为“房间开播提醒”消息,固定为 rss |
rid | 房间 id |
ss | 直播状态,0-没有直播,1-正在直播 |
code | 类型 |
rt | 开关播原因 |
rtv | 关播原因类型的值 |
notify | 通知类型 |
endtime | 关播时间(仅关播时有效) |
2.4.6 超级弹幕消息
超级弹幕消息主要部分应包含的字段如下:
type@=ssd/rid@=1/trid@=1/content@=test/cli tp@=1/url@=test_url/jmptp@=1/
字段名 | 字段说明 |
---|---|
type | 表示为“超级弹幕”消息,固定为 ssd |
rid | 房间 id |
trid | 跳转房间 id |
content | 超级弹幕的内容 |
url | 跳转 url |
clitp | 客户端类型 |
jmptp | 跳转类型 |
2.4.7 房间内礼物广播
房间内赠送礼物成功后效果主要部分应包含的字段如下:
type@=spbc/rid@=1/gfid@=1/sn@=name/dn@=name/gn@
=1/gc@=1/gb@=1/es@=1/ eid@=1/
字段名 | 字段说明 |
---|---|
type | 表示为“房间内礼物广播”,固定为 spbc |
rid | 房间 id |
sn | 赠送者昵称 |
dn | 受赠者昵称 |
gn | 礼物名称 |
gc | 礼物数量 |
drid | 赠送房间 |
gs | 广播样式 |
gb | 是否有礼包(0-无礼包,1-有礼包) |
es | 广播展现样式(1-火箭,2-飞机) |
eid | 特效 id |
bgl | 广播礼物类型 |
cl2 | 栏目分类广播字段 |
2.4.8 房间宝箱消息
用户抢宝箱房间广播部分应包含的字段如下:
type@=tsgs/rid@=1/gid@=1/gfid@=1/sn@=name/dn@=name/gn@
=1/gc@=1/gb@=1/es@=1/gfid@=1/eid@=1/
字段名 | 字段说明 |
---|---|
type | 表示为“宝箱房间广播”,固定为 tsgs |
rid | 房间 id |
rpt | 宝箱类型 |
sid | 宝箱来源用户ID |
snk | 宝箱来源用户昵称 |
did | 抢到宝箱用户ID |
dnk | 抢到宝箱用户昵称 |
silver | 抢到的鱼丸数量 |
lt | 手气类型,0-普通手气 |
lk | 是否手气王 |
pcnt | 抢到的道具数量 |
pnm | 抢到的道具名称 |
2.4.9 房间内 top10 变化消息
房间内 top10 排行榜变化后,会发送对应的消息通知,主要部分应包含的字段如下: type@=rankup/uid@=1/rn@=3/rid@=1/rkt@=1/gid@=-9999/rt@=0/ nk@=test/sz@=3/drid@=1/bt@=1/
字段名 | 字段说明 |
---|---|
type | 表示为“房间 top10 排行榜变换”,固定为 rankup |
rid | 房间 id |
uid | 用户 id |
drid | 目标房间 id |
rt | 房间所属栏目类型 |
bt | 广播类型 |
sz | 展示区域类型 |
nk | 用户昵称 |
rkt | top10 榜的类型 1-周榜 2-总榜 4-日榜 |
rn | 上升后的排名 |
2.4.10 主播离开提醒
主播直播时,中途暂时离开后,又回来继续直播,服务端发送给观众客户端的消息。主要部分应包含的字段如下: type@=al/rid@=10111/aid@=3044114/
字段名 | 字段说明 |
---|---|
type | 表示为“主播离开提醒”,固定为 al |
rid | 房间 id |
aid | 主播用户 id |
2.4.11 主播回来继续直播提醒
主播直播时,中途暂时离开后,又回来继续直播,服务端发送给观众客户端的消息。主要部分应包含的字段如下:
type@=ab/rid@=10111/gid@=-9999/aid@=3044114/
字段名 | 字段说明 |
---|---|
type | 表示为“主播回来继续直播提醒”,固定为 ab |
rid | 房间 id |
aid | 主播用户 id |
2.4.12 用户等级提升消息
用户等级提升消息,主要部分应包含的字段如下:
type@=upgrade/rid@=1/gid@=-9999/uid@=12001/nn@=test/level@
=3/ic@=icon/
字段名 | 字段说明 |
---|---|
type | 表示为“用户等级提升消息”,固定为 upgrade |
rid | 房间 id |
uid | 用户 id |
nn | 用户昵称 |
level | 用户等级 |
ic | 用户头像 |
2.4.13 主播等级提升广播
主播等级提升广播。主要部分应包含的字段如下: type@=upbc/rid@=1/gid@=-9999/lev@=20/pu@=0/
字段名 | 字段说明
- | -
type| 表示为“主播等级提升广播”,固定为 upbc
rid |房间 id
gid |弹幕分组 id
lev |主播最新等级
pu |是否需要弹窗 0-不需要,1-需要
2.4.14 禁言操作结果
chatroom 返回给前端的禁言操作结果,主要部分应包含的字段如下:
type@=newblackres/rid@=1/gid@=-9999/ret@=0/otype@=2/sid@=1
0002/did@=10003/snic@=stest/dnic@=dtest/endtime@=1501920157/
字段名 |字段说明
- | -
type | 表示为“禁言操作结果”,固定为 newblackres
rid |房间 id
ret |错误码
otype | 操作者类型,0:普通用户,1:房管:,2:主播,3:超管
sid |操作者 id
did |被操作者 id
snic |操作者昵称
dnic |被禁言用户昵称
endtime| 禁言的失效时间戳
2.4.15 徽章等级提升通知
徽章等级提升通知,主要部分应包含的字段如下:
type@=blab/rid@=1/gid@=-9999/uid@=10002/nn@=test/lbl@=2/bl
@=3/ba@=1/bnn@=ttt/
字段名 | 字段说明
- | -
type | 表示为“徽章等级提升通知”,固定为 blab
rid |房间 id
uid |用户 id
nn | 用户昵称
lbl | 上个徽章等级
bl |徽章等级
ba |广播区域字段
bnn | 徽章昵称
2.4.16 用户分享了直播间通知
用户分享了直播间通知,主要部分应包含的字段如下:
type@=srres/rid@=1/gid@=-9999/uid@=12001/nickname@=test/exp
@=3/
字段名 | 字段说明 |
---|---|
type | 表示为“用户分享了直播间通知”,固定为 srres |
rid | 房间 id |
uid | 用户(分享者)id |
nickname | 用户(分享者)昵称 |
exp | 用户获得的经验值 |
2.4.17 栏目排行榜变更通知
栏目排行榜变更通知,主要部分应包含的字段如下:
type@=rri/rid@=1/rn@=cate_rank/cate_id@=5/uid@=10005/sc@=100 00/idx@=10/bcr@=1/ibc@=1/an@=test/rktype@=1/tag_id@=1200/gif t_id@=100/
字段名 | 字段说明 |
---|---|
type | 表示为“栏目排行榜变更通知”,固定为 rri |
rid | 房间 id |
rn | 榜单名称 |
cate_id | 分类 ID |
uid | 主播 ID |
sc | 当前 member 的分数 |
idx | 当前 member 的排名 |
bcr | 人气广播的范围 |
ibc | 人气广播通知 |
an | 主播名称 |
rktype | 榜单类型, 1 - 栏目排行, 2 - 礼物周星 |
tag_id | 分类 ID |
gift_id | 礼物 id |
3 操作手册
3.1 连接初始化
1) 第三方客户端通过 TCP 协议连接到弹幕服务器(依据指定的 IP 和端口);
第三方接入弹幕服务器列表:
IP 地址:openapi-danmu.douyu.com 端口:80
2) 客户端向弹幕服务器发送登录请求,登录弹幕服务器(详见登录请求消
息);
3) 弹幕服务器收到客户端登录请求并完成登录后,返回登录成功消息给客
户端(详见登录回复消息);
4) 客户端收到登录成功消息后发送进入弹幕分组请求给弹幕服务器(详见
入组消息);
5) 弹幕服务器接受到客户端弹幕分组请求后将客户端添加到请求指定的弹幕分组中;
3.2 服务过程
1) 客户端每隔 45 秒发送心跳信息给弹幕服务器 (详见客户端心跳消息),弹幕服务器回复心跳信息给客户端(详见服务端心跳消息);
2) 弹幕服务器若有广播消息,则推送给客户端,服务端消息协议格式详情请参见服务端消息格式。
3.3 断开连接
1) 客户端发送登出消息(注销消息)给弹幕服务器(详见登出消息);
2) 客户端关闭 TCP 连接。
3.4 连接示例
# -*- coding: utf-8 -*-
import socket
import hashlib
import time
import multiprocessing
ROOM_ID = 0 # 请填入合作的斗鱼房间ID
AID = “” # 请填入斗鱼开放平台AID
SECRET = “” # 请填入斗鱼开放平台分配的私钥
TOKEN = “” # 请填入开放平台token,2小时有效期,有效期内请勿重复获取 https://open.douyu.com/source/api/8
SERVER_DOMAIN = “openapi-danmu.douyu.com” # 弹幕服务器 域名
SERVER_PORT = 80
global gl_client #全局socket
try:
gl_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
gl_client.settimeout(5)
except Exception as e:
print(“socket创建失败\r\n”)
def init_socket():
SERVER_IP = socket.gethostbyname(SERVER_DOMAIN)
try:
print(“弹幕服务器: {}:{}”.format(SERVER_IP, int(SERVER_PORT)))
gl_client.connect((SERVER_IP, int(SERVER_PORT)))
except Exception as e:
print(“连接不上目标主机”)
gl_client.settimeout(5)
def sendData(client,content): # 格式请参考文档 https://open.douyu.com/source/api/63
data = content + ‘\0’ # 数据部结尾为’\0’
data_length = len(data)+8 # 填入的消息长度=头部长度+数据部长度(包含结尾的’\0’)
code = 689 # 消息类型(客户端发送给弹幕服务器的文本格式数据)
# 消息头部:消息长度(4字节)+消息类型(2字节)+加密字段(1字节,默认为0)+保留字段(1字节,默认为0)
head = data_length.to_bytes(4, ‘little’) + code.to_bytes(2,’little’)+ (0).to_bytes(2,’little’)
client.sendall(data_length.to_bytes(4, ‘little’) + head) # 消息长度出现两遍,二者相同
msg = (data).encode(‘utf-8’) # 使用utf-8编码 数据部分
client.sendall(bytes(msg)) # 发送数据部分
def md5(string):
m = hashlib.md5()
b = string.encode(encoding=’utf-8’)
m.update(b)
auth = m.hexdigest()
return auth
def get_auth(token):
“””
Auth 生成方式为 md5({secret}{aid}{time}{token}), secret为aid对应的秘钥
“””
ts = int(time.time())
auth_string = SECRET + ““ + AID + ““ + str(ts) + ““ + token
auth = md5(auth_string)
return ts, auth
def get_danmu(client):
token = TOKEN
ts, auth = get_auth(token)
login = ‘type@=loginreq/roomid@={}/aid@={}/token@={}/time@={}/auth@={}/‘
.format(ROOM_ID,AID,token,ts,auth)
print(“登陆请求消息:\n{}”.format(login))
sendData(client,login)
joingroup = 'type@=joingroup/rid@={}/aid@={}/token@={}/time@={}/auth@={}/'\
.format(ROOM_ID,AID,token,ts,auth)
print("入组消息:\n{}".format(joingroup))
sendData(client,joingroup)
while True:
try:
read_length_byte = client.recv(4)
read_length = int.from_bytes(read_length_byte, "little")
print("接收消息长度:", read_length)
part_body = client.recv(read_length)
if len(part_body) < 9:
print("无效的消息长度:", len(part_body))
print("接收消息为:{}".format(part_body))
break
print("接收消息内容:{}".format(str(part_body[8:-1], 'utf-8')))
if not part_body: #如果 服务器发送终止连接b'',则终止会话
break
except Exception as e:
print("未知错误: %s" % e)
continue
def keep_alive(client):
‘’’ 客户端每隔 30 秒发送心跳信息给弹幕服务器 ‘’’
while True:
alive_msg = “type@=mrkl/“
sendData(client,alive_msg)
time.sleep(30)
if name == ‘main‘:
init_socket()
p1 = multiprocessing.Process(target=get_danmu, args=(gl_client,))
p2 = multiprocessing.Process(target=keep_alive, args=(gl_client,))
p1.start()
p2.start()
3.5 常见问题Q&A
Q: 为什么正常连上了直播间但却接收不到弹幕?
A: 加入房间分组才能收到消息,客户端必须发送joingroup请求
Q: 报错1043
A: 未申请“获取弹幕消息”接口的权限或权限已过期