详解-斗鱼弹幕API-接入(斗鱼弹幕服务器第三方接入协议)
本文基于"斗鱼弹幕服务器第三方接入协议V1.6.2"编写
基础准备
接入方式:Socket
API服务地址:openbarrage.douyutv.com:8601
斗鱼私有协议说明
如上图所示,每次发送的消息或者接收到的消息都会由 消息长度 + 消息长度 + 消息类型 + 真实消息内容 + 结尾标识 组成
消息 = 消息长度(4) + 消息长度(4) + 消息类型(4) + 真实消息内容(?) + 结尾标识长度(1)
其中这里要特别说明三个点
一、消息长度的算法,消息长度 = 消息长度(4) + 消息类型(4) + 真实消息内容长度 + 结尾标识长度(1)
二、虽然消息里有 两个消息长度字段,但是如上面的算法所示,计算消息长度是 只需要加 一个 消息长度(4) 字段的长度即可。
三、斗鱼要求的消息长度都为 4 字节小端整数,java中都是大端整数,所以需要通过特别方法转换,我下面都给处理对应的工具代码
int contenLeng = 4 + 4 + content.length() + 1; //消息长度算法
下面开始实际进行接入,先上代码
示例代码
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket = new Socket("openbarrage.douyutv.com", 8601);
//发送登录请求(登入9999房间)
String loginCMD = "type@=loginreq/roomid@=9999/";
send(loginCMD, socket);
//读取登录请求消息
byte[] bytes = read(socket);
String msg = new String(Arrays.copyOfRange(bytes, 0, bytes.length));
System.out.println(msg);
//加入弹幕分组开始接收弹幕
String joinGroupCMD = "type@=joingroup/rid@=9999/gid@=-9999/";
send(joinGroupCMD, socket);
//循环读取弹幕消息开始
while (true){
byte[] msgBytes = read(socket);
String s = new String(Arrays.copyOfRange(msgBytes, 0, msgBytes.length));
System.out.println(s);
Thread.sleep(1);
}
//关闭链接
//socket.close();
}
如上面的代码所示,根据斗鱼的弹幕协议的要求,每次想接收弹幕必须
1、进行房间的登录
2、加入任意分组(全量弹幕分组:-9999)
然后即可可进行弹幕消息的接收。
其中 登录 的 API 为 type@=loginreq/roomid@=9999/
加入分组的 API 为 type@=joingroup/rid@=9999/gid@=-9999/
rid 表示房间ID,一般直接用房间号即可
当然根据要求 每隔 45秒 还得发送一次心跳消息进行心跳保持,我这并没有实现,请注意自己补充
全部示例代码
import com.yycdev.douyu.sdk.exceptions.DouYuSDKException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket = new Socket("openbarrage.douyutv.com", 8601);
//发送登录请求(登入9999房间)
String loginCMD = "type@=loginreq/roomid@=9999/";
send(loginCMD, socket);
//读取登录请求消息
byte[] bytes = read(socket);
String msg = new String(Arrays.copyOfRange(bytes, 0, bytes.length));
System.out.println(msg);
//加入弹幕分组开始接收弹幕
String joinGroupCMD = "type@=joingroup/rid@=9999/gid@=-9999/";
send(joinGroupCMD, socket);
//循环读取弹幕消息开始
while (true){
byte[] msgBytes = read(socket);
String s = new String(Arrays.copyOfRange(msgBytes, 0, msgBytes.length));
System.out.println(s);
Thread.sleep(1);
}
//关闭链接
//socket.close();
}
/**
* 发送消息
*
* @param content
*/
public static void send(String content, Socket socket) {
try {
//计算消息长度 = 消息长度(4) + 消息类型(4) + 真实消息内容长度 + 结尾标识长度(1)
int contenLeng = 4 + 4 + content.length() + 1;
//小端模式转换init (长度1)
byte[] contenLeng1 = intToBytesLittle(contenLeng);
//小端模式转换init (长度2)
byte[] contenLeng2 = intToBytesLittle(contenLeng);
//小端模式转换init (消息类型) (689:客户端发送给弹幕服务器的文本格式数据)
byte[] msgType = intToBytesLittle(689);
//标识数据结尾
int end = 0;
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
//写入长度1
byteArray.write(contenLeng1);
//写入长度2(与长度1相同)
byteArray.write(contenLeng2);
//写入消息类型
byteArray.write(msgType);
//写入消息内容
byteArray.write(content.getBytes("ISO-8859-1"));
//写入数据结尾标识
byteArray.write(end);
//发送数据
OutputStream out = socket.getOutputStream();
out.write(byteArray.toByteArray());
out.flush();
} catch (IOException e) {
throw new DouYuSDKException(e);
}
}
/**
* 读取消息
*
* @return
*/
public static byte[] read(Socket socket) {
try {
InputStream inputStream = socket.getInputStream();
//下条信息的长度
int contentLen = 0;
//读取前4个字节,得到数据长度
byte[] bytes1 = readStream(inputStream,0,4);
contentLen = bytesToIntLittle(bytes1, 0); //用小端模式转换byte数组为
//System.out.println("数据长度1:" + contentLen);
//继续读取4个字节,得到第二个 数据长度
byte[] bytes2 = readStream(inputStream,0,4);
int contentLen2 = bytesToIntLittle(bytes2, 0);
//System.out.println("数据长度2:" + contentLen2);
//再次读取4个字节,得到消息类型
byte[] bytes3 = readStream(inputStream,0,4);
//将小端整数转换为大端整数
int msgType = bytesToIntLittle(bytes3, 0);
//System.out.println("消息类型:" + msgType);
//
contentLen = contentLen - 8;
//继续读取真正的消息内容
int len = 0; //本次读取数据长度
int readLen = 0; //已读数据长度
byte[] bytes = new byte[contentLen];
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
while ((len = inputStream.read(bytes, 0, contentLen - readLen)) != -1) {
byteArray.write(bytes, 0, len);
readLen += len;
if (readLen == contentLen) {
break;
}
}
return byteArray.toByteArray();
} catch (IOException e) {
throw new DouYuSDKException(e);
}
}
/**
* 计算消息体长度
*/
private static int calcMessageLength(String content) {
return 4 + 4 + (content == null ? 0 : content.length()) + 1;
}
/**
* 以小端模式将int转成byte[]
*
* @param value
* @return
*/
public static byte[] intToBytesLittle(int value) {
byte[] src = new byte[4];
src[3] = (byte) ((value >> 24) & 0xFF);
src[2] = (byte) ((value >> 16) & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
src[0] = (byte) (value & 0xFF);
return src;
}
/**
* 以小端模式将byte[]转成int
*/
public static int bytesToIntLittle(byte[] src, int offset) {
int value;
value = (int) ((src[offset] & 0xFF)
| ((src[offset + 1] & 0xFF) << 8)
| ((src[offset + 2] & 0xFF) << 16)
| ((src[offset + 3] & 0xFF) << 24));
return value;
}
/**
* 从流中读取数据
* @param inputStream
* @param off
* @param len
* @return
* @throws IOException
*/
public static byte[] readStream(InputStream inputStream, int off, int len) throws IOException {
byte[] bytes = new byte[len];
inputStream.read(bytes, 0, 4);
return bytes;
}
}
对了顺便推广一下我已经封装好的斗鱼弹幕SDK,欢迎各位直接对接使用
gitee: https://gitee.com/yycdev/douyusdk
github: https://github.com/yyc-dev/douyu-sdk
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- spring之使用外部属性文件(连接数据库时使用)
- 【python-leetcode92-翻转链表】反转链表2
- 细品Redis高性能数据结构之hash对象
- pyspark之常用算子
- linux之shell综合例子之定时任务
- spring之配置单例的集合bean,以供多个bean进行引用
- 【python-leetcode25-翻转链表】K 个一组翻转链表
- spring之通过实例工厂方法配置Bean
- 细品redis分布式锁
- 【python-leetcode102-树的宽度遍历】二叉树的层次遍历
- 【python-树的宽度遍历】二叉树的反向层次遍历
- 基于TypeScript封装Axios笔记(二)
- redis高性能数据结构之有序集
- spring之通过静态工厂方法配置Bean
- 【python-leetcode107-树的宽度遍历】二叉树的层次遍历Ⅱ