【Visual C#】基于《斗鱼弹幕服务器第三方接入协议v1.6.2》实现斗鱼弹幕服务器接入
2021-03-11 05:28
标签:tick 指针 amp layout 时间 val 网上 pytho 设置 最近在给某个主播开发斗鱼直播间辅助工具,为了程序的高效稳定,也搜索了大量的资料,经过大量什么百度,谷歌搜索。。。 虽然有很多Python的脚本及JS脚本实现了拉取斗鱼弹幕信息,但是这些年来的开发职业病告诉我,这满足不了对系统的控制欲望。。 后来,找啊。。。找啊。。。意外间发现这个文档。。。。废话不多说了,说正题吧。 斗鱼很人性化的提供了一个基于Socket TCP传输协议的标准文档,通过接口我们可以安全稳定高效的获取斗鱼直播间弹幕信息,实现多种多样化的辅助功能。 一、协议组成 众所周知,受TCP最大传输单(MTU)限制及连包机制影响,应用层协议需自己设计协议头,以保证不同消息的隔离性和消息完整性。 斗鱼后台协议头设计如下: 斗鱼消息协议格式如上所示,其中字段说明如下: 消息长度:4字节小端整数,表示整条消息(包括自身)长度(字节数)。消息长度出现两遍,二者相同。 消息类型:2字节小端整数,表示消息类型。取值如下: 689 客户端发送给弹幕服务器的文本格式数据 690 弹幕服务器发送给客户端的文本格式数据。 加密字段:暂未使用,默认为0。 保留字段:暂未使用,默认为0。 数据部分:斗鱼独创序列化文本数据,结尾必须为 ‘\0’。详细序列化、反序列化算法见下节。(所有协议内容均为UTF-8编码) 二、序列化 为增强兼容性、可读性斗鱼后台通讯协议采用文本形式的明文数据。同时针对平台数据特点,斗鱼自创序列化、反序列化算法。即STT序列化。下面详细 介绍STT序列化和反序列化。STT序列化支持键值对类型、数组类型(意外发现有的报文还有JSON类型)。规定如下: 1、键key和值value直接采用 ‘@=‘分割 2、数组采用 ‘/‘ 分割 3、如果key或者value中含有字符 ‘/‘,则使用 ‘@S‘ 转义 4、如果key或者value中含有字符 ‘@‘,则使用 ‘@A‘ 转义 举例: (1)多个键值对数据:key1@=value1/key2@=value2/key3@=value3/ (2)数组数组:value1/value2/value3/ 不同消息有相同的协议头、序列化方式。 三、客户端消息格式(部分) 1、登录请求消息 该消息用于完成登陆授权,完整的数据部分应包含的字段如下: type@=loginreq/roomid@=58839/ type:表示登陆请求消息,固定为loginreq roomid:登陆房间的ID 2、客户端心跳消息 该消息用于维持与后台间的心跳,完整的数据部分应包含的字段如下: type@=mrkl/ type:表示心跳消息,固定为mrkl 3、加入房间分组消息 该消息用于完成加入房间分组,完整的数据部分应包含的字段如下: type@=joingroup/rid@=59872/gid@=-9999/ type:表示为加入房间分组消息,固定为joingroup rid:所登录的房间号 gid:分组号,第三方平台建议选择-9999(即海量弹幕模式) 4、登出消息 type@=logout/ 该消息用于完成登出后台服务,完整的数据部分应包含的字段如下: type:表示为登出消息,固定为logout 三、实现斗鱼直播弹幕服务器API 现在网上可以轻松找到《斗鱼弹幕服务器第三方接入协议v1.6.2》接口文档,在文档中有提到两个重要的数据: 弹幕服务器地址:openbarrage.douyutv.com 弹幕服务器端口:8601 我们可以通过.NET Framework 提供的TcpClient类库来方便连接SOCKET弹幕服务器。为了实现服务的稳定性,我这里使用了异步SOCKET客户端完成连接。 1、弹幕服务器报文头: 2、异步套接字格式 3、SOCKET帮助类 这个类封装了直接通过NetworkStream对象并格式化报文向斗鱼发送报文(仅仅为了提高开发效率) 这里可能会有人问到 StreamSerializationHelper这个类库从哪里来的,这个是自己写的一个实现对struct结构体序列化的方法。下面也提供一下,如果有更好的可自行更换:) 4、实现登陆弹幕服务器代码如下: 好了,上面就是基本全部代码了,具体的自行研究吧,有空的话提供大家一些报文的详情数据。 【Visual C#】基于《斗鱼弹幕服务器第三方接入协议v1.6.2》实现斗鱼弹幕服务器接入 标签:tick 指针 amp layout 时间 val 网上 pytho 设置 原文地址:https://www.cnblogs.com/briny/p/12653625.html
字节
Byte0
Byte 1
Byte 2
Byte 3
长度
消息长度
头部
消息长度
消息类型
加密字段
保留字段
数据部
数据部分(结尾必须为 ‘\0‘)
///
//
#region SOCKET帮助类
///
///
///
/// #region 私有变量
int dwMrkl = Environment.TickCount; // 记录执行的时间,因为斗鱼规定每45秒要向斗鱼发送一次心跳消息(否则踢下线)
#endregion
#region 连接弹幕
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("openbarrage.douyutv.com",8601);
#endregion
#region 网络数据
using (NetworkStream ms = tcpClient.GetStream())
{
#region 登陆请求
SocketHelper.LiveMessagePush(string.Format("type@=loginreq/roomid@={0}/\0", 99999), ms);
#endregion
#region 接收数据
while (environment_semaphore && tcpClient.Connected)
{
#region 发送心跳
if (!ms.DataAvailable && tcpClient.Connected)
{
// 不管是否有数据,只要SOCKET连接那么就进行心跳判断
if (Environment.TickCount - dwMrkl >= 45000)
{
dwMrkl = Environment.TickCount; // 重新计算心跳消息时间
SocketHelper.LiveMessagePush("type@=mrkl/\0", ms);
}
Thread.Sleep(5);
continue;
}
#endregion
#region 发送心跳
if (Environment.TickCount - dwMrkl >= 45000)
{
dwMrkl = Environment.TickCount;
SocketHelper.LiveMessagePush("type@=mrkl/\0", ms);
}
#region 数据处理
byte[] buffer = new byte[SOCKET_PACKAGE.BufferSize];
ms.Read(buffer, 0, buffer.Length);
int dwLen = BitConverter.ToInt32(buffer, 0);
int unReadOfBytes = dwLen;
#endregion
#region 报文处理
using (MemoryStream s = new MemoryStream())
{
#region 粘包处理
// 大家都知道TCP有粘包数据,因为不是优雅的一问一答式,所以要自行处理,这是我想到的最简单处理粘包的办法咯
do
{
buffer = new byte[unReadOfBytes >= 1024 ? 1024 : unReadOfBytes];
int dwBytesOfRead = ms.Read(buffer, 0, buffer.Length);
s.Write(buffer, 0, dwBytesOfRead);
unReadOfBytes -= dwBytesOfRead;
} while (unReadOfBytes > 0);
s.Position = 0;
#endregion
#region 报文处理
if (true)
{
string content = Encoding.UTF8.GetString(s.ToArray(), 8, dwLen - 8);
foreach (string target in Regex.Split(content, "/", RegexOptions.IgnoreCase))
{
if (!string.IsNullOrWhiteSpace(target))
{
string[] items = Regex.Split(target, "@=", RegexOptions.IgnoreCase);
if (string.Compare("type", items[0], true) == 0 && string.Compare("loginres", items[1], true) == 0)
{
// 当我们收到loginres消息后再发送加入房间分组消息
SocketHelper.LiveMessagePush(string.Format("type@=joingroup/rid@={0}/gif@=-9999/\0", 99999), ms);
}
if (string.Compare("type", items[0], true) == 0 && string.Compare("loginres", items[1], true) != 0)
{
string message_type = items[1].Replace("@S", "/").Replace("@A", "@");
if (!string.IsNullOrWhiteSpace(message_type) && string.Compare("mrkl", message_type, true) != 0)
{
// 这里拿到的content数据就是不含心跳报文的数据,具体要怎么处理看你自己需求了
// TO DO :
}
}
}
}
}
#endregion
}
#endregion
}
#endregion
}
#endregion
上一篇:WPF实现消息提醒(广告弹窗)
下一篇:Delphi计算程序运行时间
文章标题:【Visual C#】基于《斗鱼弹幕服务器第三方接入协议v1.6.2》实现斗鱼弹幕服务器接入
文章链接:http://soscw.com/index.php/essay/63054.html