使用Workerman接入Bilibili直播弹幕协议她和她的猫hercat

逛B站的时候,突然想到可以用PHP接入直播弹幕,然后在命令行显示弹幕消息。

弹幕协议由头部和数据组成,头部的长度是固定的16字节,数据的长度=数据包总长度-头部的长度。

协议的字节序均为大端模式。高字节在低地址,低字节在高地址,比如0x1234,在大端模式下存储是0x120x34,在小端模式下是0x340x12。

下面是弹幕协议的格式。

字段对照表:

常量列是对应的值在代码中的常量名。

/***头部长度*/constHEADER_LEN=16;/***协议版本*/constPROTOCOL_VERSION=2;/***魔法数字,设置为1即可*/constMAGIC_NUMBER=1;打包协议先来看看打包弹幕协议的逻辑,先计算出数据包的总长度,然后将头部信息及数据打包成二进制数据。

publicstaticfunctionpack($opcode,$payload=''){$packetLen=static::HEADER_LEN;if(!empty($payload)){$packetLen+=strlen($payload);}returnpack('NnnNN',$packetLen,static::HEADER_LEN,static::PROTOCOL_VERSION,$opcode,static::MAGIC_NUMBER).$payload}pack/unpack函数这里简单讲下pack/unpack函数的使用。

pack就是将输入参数打包成指定格式的二进制数据,上面的n、N就是指定的格式,分别表示无符号短整型(16位,大端字节序)、无符号长整型(32位,大端字节序)。

第一个N就是以无符号长整型(32位,大端字节序)的格式打包数据包总长度。第二个n就是以无符号短整型(16位,大端字节序)的格式打包头部长度。第三个n就是以无符号短整型(16位,大端字节序)的格式打包协议版本号。后面的以此类推...

上面使用的是PHP可变参数的方式进行打包,也可以将每个数据单独打包最后再拼在一起,效果也是一样的。

unpack就是pack的反向操作,根据指定的格式将二进制数据解压到数组中。

每条数据以指定的格式+key的方式组成,多条数据用/分隔。

举个例子:

$data=pack('Nnn',2021,3,31);var_dump($data);$arr=unpack('Nyear/nmonth/nday',$data);var_dump($arr);//输出:string(8)"\000\000\000\000"array(3){'year'=>int(2021)'month'=>int(3)'day'=>int(31)}打包的时候是按照Nnn的格式打包的,所以解压的时候也是按照Nnn的格式来的,只不过需要在每个格式的右边指定以这个格式解压出来的数据对应的key是什么。

Nyear就是以无符号长整型(32位,大端字节序)的格式解压,并将year作为该数据的key。nmonth就是以无符号短整型(16位,大端字节序)的格式解压,并将month作为该数据的key。...

接下来看看解压弹幕协议的逻辑,其实跟上面说的一样,按照打包的顺序然后指定对应的key就可以了。

publicstaticfunctionunpack($data){if(empty($data)){return[];}returnunpack('Npacket_len/nheader_len/nprotocol_version/Nopcode/Nmagic_number/a*payload',$data);}a表示字符串,*表示任意长度,更严谨一点应该将*改为数据的长度(数据包总长度-头部长度)

constPACKET_HEADER_LEN=16;constPACKET_PROTOCOL_VERSION=2;constPACKET_MAGIC_NUMBER=1;classPacket{staticpack(opcode,payload=''){letpacket_len=PACKET_HEADER_LEN;if(payload.length>0){packet_len+=payload.length;}letbuffer=Buffer.alloc(packet_len);buffer.writeInt32BE(packet_len,0);buffer.writeInt16BE(PACKET_HEADER_LEN,4);buffer.writeInt16BE(PACKET_PROTOCOL_VERSION,6);buffer.writeInt32BE(opcode,8);buffer.writeInt32BE(PACKET_MAGIC_NUMBER,12);if(payload.length>0){buffer.write(payload,PACKET_HEADER_LEN,payload.length);}returnbuffer;}staticunpack(data){letbuffer=Buffer.from(data);return{packet_len:buffer.readInt32BE(0),header_len:buffer.readInt16BE(4),version:buffer.readInt16BE(6),opcode:buffer.readInt32BE(8),magic_number:buffer.readInt32BE(12),data:buffer.slice(PACKET_HEADER_LEN),};}}与弹幕服务器的交互接下来看看如何通过弹幕服务器的认证,并在加入房间之后维护在线状态,我将这部分逻辑都放在了BilibiliBarrage类中。

在连接弹幕服务器之前,需要通过房间id获取到弹幕服务器的地址和端口号,还有认证需要用到的token。

{"code":0,"msg":"ok","message":"ok","data":{"refresh_row_factor":0.125,"refresh_rate":100,"max_delay":5000,"port":2243,"host":"broadcastlv.chat.bilibili.com","token":"pMF5ippgZMpHIHzTsfKHp9YyqmW3yEfuIuL3pXSMXJ8_9UFN-qSRIPTRxNjpNOr5HQ2-ajI0RcSQkMud2_-lMLoEN92k1glp9fOslshF5SFDqDhlEJRAvUwezoyz72ZdNh-sSqHMPsGOJGMOXZmRNA"}}认证并加入房间通过data中的host和port就可以对弹幕服务器发起连接,连接建立后需要发送认证包加入房间。

认证包的内容:

publicstaticfunctiongetAuthenticatePacket($room_id,$token=null){if(empty($token)){$token=static::getChatConfig($room_id)['token'];}$payload=\json_encode(['uid'=>0,'roomid'=>$room_id,'protover'=>Packet::PROTOCOL_VERSION,'platform'=>'web','token'=>$token,]);returnPacket::pack(Opcode::AUTHENTICATION,$payload);}返回的内容:

\000\000\000\000\000\000\000\000\000\000\000{"uid":0,"roomid":22590309,"protover":2,"platform":"web","token":"pMF5ippgZMpHIHzTsfKHp9YyqmW3yEfuIuL3pXSMXJ8_9UFN-qSRIPTRxNjpNOr5HQ2-ajI0RcSQkMud2_-lMLoEN92k1glp9fOslshF5SFDqDhlEJRAvUwezoyz72ZdNh-sSqHMPsGOJGMOXZmRNA"}弹幕服务器收到认证包后,会回复我们加入成功的消息,Packet::unpack后得到消息内容:

array(6){'packet_len'=>int(26)'header_len'=>int(16)'protocol_version'=>int(2)'opcode'=>int(8)'magic_number'=>int(1)'payload'=>string(10)"{"code":0}"}opcode为8表示是服务器发送的心跳包,payload是一个JSON字符串,code为0表示连接成功。

这一步完成之后就可以收到弹幕消息了,但是还差最后一步。

弹幕服务器要求每隔30秒发送一次心跳包,以确定客户端还处于活跃状态。

心跳包没有数据,只需要发送opcode为2的数据包就可以了。

可以使用Workerman、Swoole甚至PHP原生socket来实现弹幕客户端,那为啥要用Workerman呢?

简单、方便,最重要的是写起来快,不用装扩展也没有原生socket那么繁杂,三两下就写完了。

由于篇幅的原因,我会摘取重要的部分来讲,完整的代码可以去GitHub获取完整代码。

话不多说,干就完了。

Worker进程启动后,通过AsyncTcpConnection创建异步TCP连接对象。

在onConnect回调中发送认证包、开启定时任务,每隔20秒发送一次心跳包。

$room_id=22590309;/*获取直播间配置*/$config=BilibiliBarrage::getChatConfig($room_id);/*创建异步TCP连接对象*/$conn=newAsyncTcpConnection("tcp://{$config['host']}:{$config['port']}");$conn->onConnect=function(TcpConnection$conn)use($room_id,$config){$packet=BilibiliBarrage::getAuthenticatePacket($room_id,$config['token']);/*发送认证包*/$result=$conn->send($packet,true);if(!$result){Worker::safeEcho("发送认证包失败\n");return;}/*开启定时任务*/Timer::add(BilibiliBarrage::HEART_BEAT_INTERVAL,function(TcpConnection$conn){/*发送心跳包*/$conn->send(BilibiliBarrage::getHeartBeatPacket(),true);},[$conn]);};处理弹幕消息在onMessage回调中,先unpack数据,通过opcode判断本次消息是做什么的,不同的消息做不同的处理。如果opcode为CMD,需要通过Packet::parsePayload解析数据才能得到真正的消息内容。

注意!!!本文及源码仅用于学习研究!请勿用于商业或非法目的,否则后果自负。

THE END
1.“与你谈科学”系列课程:曹云霞教授讲授“从辅助生殖看未来人类...六部部颁教材的主编、副主编和编委。第十二届省人大常委会委员。安徽省科协副主席(兼)。 直播观看方式: 1.超星观看地址:https://zhibo.chaoxing.com/8880791(二维码见下) 2.bilibili上海交通大学直播间(二维码见下) 安徽医科大学研究生学院 2020年12月3日...https://www.ahmu.edu.cn/2020/1203/c4328a86998/page.psp
2.CCTV1综合频道高清直播CCTV节目官网视频小央视频小央直播直播中国熊猫频道VR/AR4K专区全景新闻 现场前线比划快看蓝海中国新兵请入列人生第一次人生第二次 体育直播竞猜巴黎奥运会欧洲杯CBANBA中超国足国际足球网球 VIP会员CCTV奥林匹克频道生活体育大会体育江湖文化体育冰雪道路足球道路 财经教育乡村振兴生态环境一带一路央博文化旅游美食海洋健康乐龄阅读汽车...https://tv.cctv.com/live/cctv1/index.shtml
3.哔哩哔哩直播姬(com.bilibili.bilibililive)6.49.0直播B站bilibili哔哩哔哩二次元宅 详细信息 应用包名:com.bilibili.bilibililive 更新时间:6天前 支持ROM:4.0.3+ 开发者名称:上海幻电信息科技有限公司 权限信息 · 查看网络连接 · 在其他应用之上显示内容 · 完全的网络访问权限 · 修改或删除您的USB存储设备中的内容 ...https://www.coolapk.com/apk/com.bilibili.bilibililive?from=rss&version=1.9.1
1.哔哩哔哩直播姬下载bilibili直播姬4.56.0.6136bilibili直播姬B站官方推出的一款直播软件,只管卖萌其他的交给直播姬吧,集成弹幕、一键开播、高清播放、快捷管理,bilibili直播姬一款简单实用的高清直播软件,不管是游戏还是各种三次元都能直播。 【功能介绍】 一键开播功能,登录后可直接推流至哔哩哔哩直播间,无需网页上进行操作。 https://xiazai.zol.com.cn/detail/44/438971.shtml
2.使用python采集bilibili直播弹幕bilibili获取弹幕本文介绍了如何使用Python的requests库从Bilibili直播间收集实时弹幕数据,通过模拟浏览器请求、解析JSON并将其保存至CSV文件的过程,以避免被服务器识别为机器人请求。 摘要由CSDN通过智能技术生成 从Bilibili直播间中收集直播弹幕数据,并将其保存到CSV文件中。 https://blog.csdn.net/hulknnn/article/details/138202368
3....自动回复工具,房管工具,自动打卡,Bilibili直播弹幕姬(使用...是否仅在直播中开启 可调节每次感谢人数 可屏蔽天选时刻下的关注 延迟感谢(可统计延时内关注,一旦延时内有新关注就会重新刷新延时)! 可设置多条感谢弹幕模板 进入直播间欢迎感谢姬 是否仅在直播中开启 可调节每次感谢人数 可屏蔽天选时刻下的感谢 延迟感谢(可统计延时内关注,一旦延时内有新进入直播间观众就会重新刷新...https://github.com/BanqiJane/Bilibili_Danmuji
4.怎样申请哔哩哔哩直播间申请bilibili直播间详细图文教程新手学堂bilibili直播间申请图文教程 一、实名认证 1、需要绑定手机并经过实名认证才能获得直播权限,实名认证时需要上传您本人的身份证反面照片+手持身份证照片。 2、已完成实名认证的用户,请登录直播页面点击【我要直播】按钮根据提示完成开通直播的申请,提交后请等待审核通过。 https://www.jb51.net/softjc/460557.html