涉及相关的资料可以去斗鱼开发者平台上找
http://dev-bbs.douyutv.com/forum.php
以下内容改编自斗鱼开发者平台上的JAVA版弹幕获取
BarrageActivity.java 界面
斗鱼弹幕服务器地址和端口号
private final String hostname ="openbarrage.douyutv.com";
private final int port =8601;
连接弹幕服务器
private void ConnectServer()
{
try {
//获取弹幕服务器访问host
String host = InetAddress.getByName(hostname).getHostAddress();
//建立socke连接
socket = new Socket(host,port);
//设置socket输入及输出
in = new BufferedInputStream(socket.getInputStream());
out = new BufferedOutputStream(socket.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
进入斗鱼指定房间号
private void EnterRoom(int roomId)
{
try {
//获取弹幕服务器登陆请求数据包
byte[] loginRequestData = DyMessage.getLoginRequestData(roomId);
//发送登陆请求数据包给弹幕服务器
out.write(loginRequestData, 0, loginRequestData.length);
out.flush();
//初始化弹幕服务器返回值读取包大小
byte[] recvByte = new byte[MAX_BUFFER_LENGTH];
//获取弹幕服务器返回值
in.read(recvByte, 0, recvByte.length);
//解析服务器返回的登录信息
if(DyMessage.parseLoginRespond(recvByte)){
} else {
}
}catch (Exception e) {
e.printStackTrace();
}
}
加入弹幕分组池
private void JoinGroup(int roomId, int groupId)
{
//获取弹幕服务器加弹幕池请求数据包
byte[] joinGroupRequest = DyMessage.getJoinGroupRequest(roomId, groupId);
try{
//向弹幕服务器发送加入弹幕池请求数据
out.write(joinGroupRequest, 0, joinGroupRequest.length);
out.flush();
} catch(Exception e){
e.printStackTrace();
}
}
获取服务器返回信息
public void getServerMsg()
{
//初始化获取弹幕服务器返回信息包大小
byte[] recvByte = new byte[MAX_BUFFER_LENGTH];
//定义服务器返回信息的字符串
String dataStr;
try {
//读取服务器返回信息,并获取返回信息的整体字节长度
int recvLen = in.read(recvByte, 0, recvByte.length);
//根据实际获取的字节数初始化返回信息内容长度
byte[] realBuf = new byte[recvLen];
//按照实际获取的字节长度读取返回信息
System.arraycopy(recvByte, 0, realBuf, 0, recvLen);
//根据TCP协议获取返回信息中的字符串信息
dataStr = new String(realBuf, 12, realBuf.length - 12);
//循环处理socket黏包情况
while(dataStr.lastIndexOf("type@=") > 5)
{
//对黏包中最后一个数据包进行解析
MsgView msgView = new MsgView(dataStr.substring(dataStr.lastIndexOf("type@=")));
//分析该包的数据类型,以及根据需要进行业务操作
parseServerMsg(msgView.getMessageList());
//处理黏包中的剩余部分
dataStr = dataStr.substring(0, dataStr.lastIndexOf("type@=") - 12);
}
//对单一数据包进行解析
MsgView msgView = new MsgView(dataStr.substring(dataStr.lastIndexOf("type@=")));
//分析该包的数据类型,以及根据需要进行业务操作
parseServerMsg(msgView.getMessageList());
} catch (Exception e) {
e.printStackTrace();
}
}
解析从服务器接受的协议,并根据需要订制业务需求
private void parseServerMsg(Map<String, Object> msg)
{
if(msg.get("type") != null)
{
//服务器反馈错误信息
if(msg.get("type").equals("error"))
{
}
//判断消息类型
if(msg.get("type").equals("chatmsg"))
{
//弹幕消息
AddBarrage(msg.get("nn") + ":" + msg.get("txt").toString());
}
else if(msg.get("type").equals("dgb"))
{
//赠送礼物信息
}
else
{
}
}
}
服务器心跳连接,每隔45秒就要发送心跳信息给服务器,以表示还处于连接中
public void keepAlive()
{
//获取与弹幕服务器保持心跳的请求数据包
byte[] keepAliveRequest = DyMessage.getKeepAliveData((int)(System.currentTimeMillis() / 1000));
try{
//向弹幕服务器发送心跳请求数据包
out.write(keepAliveRequest, 0, keepAliveRequest.length);
out.flush();
} catch(Exception e){
e.printStackTrace();
}
}
DyMessage.java 斗鱼弹幕协议信息封装类
生成登录请求数据包
public static byte[] getLoginRequestData(int roomId)
{
//编码器初始化
DyEncoder enc = new DyEncoder();
//添加登录协议type类型
enc.addItem("type", "loginreq");
//添加登录房间ID
enc.addItem("roomid", roomId);
//返回登录协议数据
return DyMessage.getByte(enc.getResult());
}
生成加入弹幕分组池数据包
public static byte[] getJoinGroupRequest(int roomId, int groupId)
{
//编码器初始化
DyEncoder enc = new DyEncoder();
//添加加入弹幕池协议type类型
enc.addItem("type", "joingroup");
//添加房间id信息
enc.addItem("rid", roomId);
//添加弹幕分组池id信息
enc.addItem("gid", groupId);
//返回加入弹幕池协议数据
return DyMessage.getByte(enc.getResult());
}
生成心跳协议数据包
public static byte[] getKeepAliveData(int timeStamp)
{
//编码器初始化
DyEncoder enc = new DyEncoder();
//添加心跳协议type类型
enc.addItem("type", "keeplive");
//添加心跳时间戳
enc.addItem("tick", timeStamp);
//返回心跳协议数据
return DyMessage.getByte(enc.getResult());
}
将数据转换为小端整数格式
private static byte[] getByte(String data)
{
ByteArrayOutputStream boutput = new ByteArrayOutputStream();
DataOutputStream doutput = new DataOutputStream(boutput);
try
{
boutput.reset();
// 4 bytes packet length
doutput.write(FormatTransfer.toLH(data.length() + 8), 0, 4);
// 2 bytes message type
doutput.write(FormatTransfer.toLH(DY_MESSAGE_TYPE_CLIENT), 0, 2);
// 1 bytes encrypt
doutput.writeByte(0);
// 1 bytes reserve
doutput.writeByte(0);
doutput.writeBytes(data);
}
catch(Exception e)
{
e.printStackTrace();
}
return boutput.toByteArray();
}
MsgView.java 弹幕协议解析类
public class MsgView
{
private Map<String, Object> messageList;
public MsgView(String data){
this.messageList = parseRespond(data);
}
//获取弹幕信息对象
public Map<String, Object> getMessageList() {
return messageList;
}
//设置弹幕信息对象
public void setMessageList(Map<String, Object> messageList) {
this.messageList = messageList;
}
//解析弹幕服务器接收到的协议数据
public Map<String, Object> parseRespond(String data)
{
Map<String, Object> rtnMsg = new HashMap<String, Object>();
//处理数据字符串末尾的'/0字符'
data = data.substring(0,data.lastIndexOf("/"));
//对数据字符串进行拆分
String[] buff = data.split("/");
//分析协议字段中的key和value值
for(String tmp : buff)
{
//获取key值
String key = tmp.substring(0, tmp.indexOf("@="));
//获取对应的value值
Object value = tmp.substring(tmp.indexOf("@=")+2, tmp.length());
//如果value值中包含子序列化值,则进行递归分析
if(((String)value).contains("@A"))
{
value = ((String)value).replaceAll("@S", "/").replaceAll("@A", "@");
value = this.parseRespond((String)value);
}
//将分析后的键值对添加到信息列表中
rtnMsg.put(key, value);
}
return rtnMsg;
}
}