首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >详解-斗鱼弹幕API-接入(斗鱼弹幕服务器第三方接入协议)

详解-斗鱼弹幕API-接入(斗鱼弹幕服务器第三方接入协议)

作者头像
用户1688446
发布于 2019-03-12 08:44:58
发布于 2019-03-12 08:44:58
4.3K00
代码可运行
举报
运行总次数:0
代码可运行

本文基于"斗鱼弹幕服务器第三方接入协议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; //消息长度算法

下面开始实际进行接入,先上代码

示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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秒 还得发送一次心跳消息进行心跳保持,我这并没有实现,请注意自己补充

全部示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/08/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
你应该知晓的Rust Web 框架
在之前的用 Rust 搭建 React Server Components 的 Web 服务器我们利用了Axum构建了RSC的服务器。也算是用Rust在构建Web服务上的小试牛刀。
前端柒八九
2023/11/17
3.2K0
你应该知晓的Rust Web 框架
服务端常用的WebSocket框架
输入命令 需要修改的 url、groupId、artifactId、version
码客说
2021/01/20
1.5K0
Rust:axum学习笔记(7) websocket
核心就是handle_socket这个function,这里我们只是简单的将收到的内容,原封不动的发回浏览器。
菩提树下的杨过
2022/04/27
2.3K1
Rust:axum学习笔记(7) websocket
Python 实现 WebSocket 通信
WebSocket 协议主要用于解决Web前端与后台数据交互问题,在WebSocket技术没有被定义之前,前台与后端通信需要使用轮询的方式实现,WebSocket则是通过握手机制让客户端与服务端建立全双工通信,从而实现了更多复杂的业务需求。
王 瑞
2022/12/28
2K0
开源的C#实现WebSocket协议客户端和服务器websocket-sharp组件解析
彭泽0902
2018/01/04
15.3K1
2023 年值得关注的 6 个 Rust web开发框架
在 2023 年,在开始开发 API 和 Web 应用程序之前,哪个 Rust Web 框架最适合研究?在这里,我们将看看一些目前使用或看起来很有前途的最流行的框架。
程序那些事儿
2023/03/07
10.5K0
2023 年值得关注的 6 个 Rust web开发框架
一文读懂 WebSocket 通信过程与实现
来源:Python那些事 ID:PythonSomething 什么是 WebSocket ? WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现。 以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较高。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一
小小科
2018/06/20
2.3K0
一文读懂 WebSocket 通信过程与实现
WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现。
前端教程
2018/07/27
7550
一文读懂 WebSocket 通信过程与实现
JavaScript 连接消息(RabbitMQ)
STOMP是一个简单的面向文本的消息传递协议。它定义了一种可互操作的有线格式 ,以便任何可用的 STOMP 客户端都可以与任何 STOMP 消息代理进行通信,从而在语言和平台之间提供简单而广泛的消息互操作性(STOMP 网站有一个STOMP 客户端和服务器实现列表。
鱼找水需要时间
2023/02/16
8100
JavaScript 连接消息(RabbitMQ)
深度:手写一个WebSocket协议 [7000字]
我之前是做IM相关桌面端软件的开发,基于TCP长链接自己封装的一套私有协议,目前公司也有项目用到了ws协议,好像无论什么行业,都会遇到这个ws协议。
Peter谭金杰
2020/05/09
1.6K0
深度:手写一个WebSocket协议 [7000字]
springboot vue整合websocket
1.引入包 compile 'org.springframework.boot:spring-boot-starter-websocket' 2.新建类MyWebsocket import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import ja
似水的流年
2018/07/04
2.2K0
腾讯云CDN支持WebSocket
Websocket是用于服务端主动向客户端推送消息的技术。传统的HTTP/HTTPS只能由客户端向服务端发起请求,服务端对请求一一响应。在需要获取服务端状态变化的场景下,如:提交的后台任务是否执行成功,只能通过客户端轮询向服务端发起请求,不仅效率低,还浪费资源(HTTP1.0下每次轮询都需要经过TCP三次握手重新建立连接)。而WebSocket的出现较好的解决了这个问题,在TCP首次建立完连接之后,该连接不自动关闭,在有效期内客户端可以继续向服务端发送消息,服务端也能主动给客户端发送消息。
yaho
2020/05/02
24.8K0
腾讯云CDN支持WebSocket
php实现websocket实时消息推送
软件通信有七层结构,下三层结构偏向与数据通信,上三层更偏向于数据处理,中间的传输层则是连接上三层与下三层之间的桥梁,每一层都做不同的工作,上层协议依赖与下层协议。基于这个通信结构的概念。
OwenZhang
2021/12/08
2.5K0
php实现websocket实时消息推送
【websocket】spring boot 集成 websocket 的四种方式
这个配置类很简单,通过这个配置 spring boot 才能去扫描后面的关于 websocket 的注解
猿天地
2019/10/31
44.7K2
HTML5中的WebSocket
在传统方式下,很多网站为了实现即时通讯,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对伺服器发出HTTP request,然后由伺服器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向伺服器发出请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。
用户7353950
2022/05/10
1.3K0
HTML5中的WebSocket
PHP是如何实现websocket实时消息推送的
什么是WebSocket WebSocket用于在Web浏览器和服务器之间进行任意的双向数据传输的一种技术。WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输过程。 其目的是在WebSocket应用和WebSocket服务器进行频繁双向通信时,可以使服务器避免打开多个HTTP连接进行工作来节约资源,提高了工作效率和资源利用率。
友儿
2022/09/11
1.2K0
Netty 系列八(基于 WebSocket 的简单聊天室).
    之前写过一篇 Spring 集成 WebSocket 协议的文章 —— Spring消息之WebSocket ,所以对于 WebSocket 协议的介绍就不多说了,可以参考这篇文章。这里只做一些补充说明。另外,Netty 对 WebSocket 协议的支持要比 Spring 好太多了,用起来舒服的多。
JMCui
2018/09/27
1.9K0
Netty 系列八(基于 WebSocket 的简单聊天室).
you-dont-know-websocket
本文阿宝哥将从多个方面入手,全方位带你一起探索 WebSocket 技术。阅读完本文,你将了解以下内容:
阿宝哥
2020/07/29
1.8K0
you-dont-know-websocket
微信小程序websocket正确姿势
1.请求地址必须是ws://或wss:// var wsurl = 'wss://zsj.itdos.net/v1' 2.服务端websocket必须返回文件头protocol 小程序端请求会发
hotqin888
2021/08/10
1.5K0
『互联网架构』软件架构-netty之websocket协议应用实践(59)
为了解决上述弊端,Html5定义了WebSocket协义能更好的节省服务器资源和宽带达到实时通信的目的。
IT架构圈
2019/05/31
8990
推荐阅读
相关推荐
你应该知晓的Rust Web 框架
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档