最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java基于websocket協(xié)議與netty實時視頻彈幕交互實現(xiàn)

 更新時間:2021年09月02日 11:13:20   作者:興趣使然的程序猿  
本文主要介紹了Java基于websocket協(xié)議與netty實時視頻彈幕交互實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

摘要

       2021年了,還有不支持彈幕的視頻網(wǎng)站嗎,現(xiàn)在各種彈幕玩法層出不窮,抽獎,ppt都上彈幕玩法了,不整個彈幕都說不過去了,今天筆者就抽空做了一個實時視頻彈幕交互功能的實現(xiàn),不得不說這樣的形式為看視頻看直播,講義PPT,抽獎等形式增加了許多樂趣。

1 技術(shù)選型

1.1 netty

       官方對于netty的描述:netty官網(wǎng).

       Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.
       Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
‘Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.1

       主要關(guān)鍵詞描述:netty是異步事件驅(qū)動網(wǎng)絡框架,可做各種協(xié)議服務端,并且支持了FTP,SMTP,HTTP等很多協(xié)議,并且性能,穩(wěn)定性,靈活性都很棒。

netty整體架構(gòu)
    

   可以看到netty整體架構(gòu)上分了三個部分:
       a. 以零拷貝,一致性接口,擴展事件模型的底層核心。
       b. Socket,Datagram,Pipe,Http Tunnel作為傳輸媒介。
       c. 傳輸支持的各種協(xié)議,HTTP&WebSocket,SSL,大文件,zlib/gzip壓縮,文本,二進制,Google Protobuf等各種各種的傳輸形式。

1.2 WebSocket

       WebSocket是一種在單個TCP連接上進行全雙工通信的協(xié)議。WebSocket通信協(xié)議于2011年被IETF定為標準RFC 6455,并由RFC7936補充規(guī)范。WebSocket API也被W3C定為標準。
       WebSocket使得客戶端和服務器之間的數(shù)據(jù)交換變得更加簡單,允許服務端主動向客戶端推送數(shù)據(jù)。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進行雙向數(shù)據(jù)傳輸。2

1.3 為什么做這樣的技術(shù)選型。

       a. 由上述可知,實時直播交互作為互動式是一個雙向數(shù)據(jù)傳輸過程。所以使用webSocket。
       b. netty本身支持了webSocket協(xié)議的實現(xiàn),讓實現(xiàn)更加簡單方便。

2 實現(xiàn)思路

2.1 服務架構(gòu)

       整體架構(gòu)是所有客戶端都和我的服務端開啟一個雙向通道的架構(gòu)。

服務結(jié)構(gòu)

2.2 傳輸流程

3 實現(xiàn)效果

3.1 視頻展示

       先看看效果吧,是不是perfect,接下來就來看具體代碼是怎么實現(xiàn)的吧。

視頻直播彈幕示例

4 代碼實現(xiàn)

4.1 項目結(jié)構(gòu)

一個maven項目,將代碼放一個包下就行。

在這里插入圖片描述

4.2 Java服務端

       Java服務端代碼,總共三個類,Server,Initailizer和 Handler。

4.2.1 先做一個netty nio的服務端:

       一個nio的服務,開啟一個tcp端口。

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Copyright(c)lbhbinhao@163.com
 * @author liubinhao
 * @date 2021/1/14
 * ++++ ______                           ______             ______
 * +++/     /|                         /     /|           /     /|
 * +/_____/  |                       /_____/  |         /_____/  |
 * |     |   |                      |     |   |        |     |   |
 * |     |   |                      |     |   |________|     |   |
 * |     |   |                      |     |  /         |     |   |
 * |     |   |                      |     |/___________|     |   |
 * |     |   |___________________   |     |____________|     |   |
 * |     |  /                  / |  |     |   |        |     |   |
 * |     |/ _________________/  /   |     |  /         |     |  /
 * |_________________________|/b    |_____|/           |_____|/
 */
public enum BulletChatServer {
    /**
     * Server instance
     */
    SERVER;

    private BulletChatServer(){
        EventLoopGroup mainGroup = new NioEventLoopGroup();
        EventLoopGroup subGroup  = new NioEventLoopGroup();
        ServerBootstrap server = new ServerBootstrap();
        server.group(mainGroup,subGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new BulletChatInitializer());
        ChannelFuture future = server.bind(9123);
    }

    public static void main(String[] args) {

    }

}

4.2.2 服務端的具體處理邏輯

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * Copyright(c)lbhbinhao@163.com
 *
 * @author liubinhao
 * @date 2021/1/14
 * ++++ ______                           ______             ______
 * +++/     /|                         /     /|           /     /|
 * +/_____/  |                       /_____/  |         /_____/  |
 * |     |   |                      |     |   |        |     |   |
 * |     |   |                      |     |   |________|     |   |
 * |     |   |                      |     |  /         |     |   |
 * |     |   |                      |     |/___________|     |   |
 * |     |   |___________________   |     |____________|     |   |
 * |     |  /                  / |  |     |   |        |     |   |
 * |     |/ _________________/  /   |     |  /         |     |  /
 * |_________________________|/b    |_____|/           |_____|/
 */

public class BulletChatInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpObjectAggregator(1024*64));
        pipeline.addLast(new IdleStateHandler(8, 10, 12));
        pipeline.addLast(new WebSocketServerProtocolHandler("/lbh"));
        pipeline.addLast(new BulletChatHandler());
    }
}


[^

       后臺處理邏輯,接受到消息,寫出到所有的客戶端:

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * Copyright(c)lbhbinhao@163.com
 *
 * @author liubinhao
 * @date 2021/1/14
 * ++++ ______                           ______             ______
 * +++/     /|                         /     /|           /     /|
 * +/_____/  |                       /_____/  |         /_____/  |
 * |     |   |                      |     |   |        |     |   |
 * |     |   |                      |     |   |________|     |   |
 * |     |   |                      |     |  /         |     |   |
 * |     |   |                      |     |/___________|     |   |
 * |     |   |___________________   |     |____________|     |   |
 * |     |  /                  / |  |     |   |        |     |   |
 * |     |/ _________________/  /   |     |  /         |     |  /
 * |_________________________|/b    |_____|/           |_____|/
 */

public class BulletChatHandler  extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    // 用于記錄和管理所有客戶端的channel
    public static ChannelGroup channels =
            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 獲取客戶端傳輸過來的消息
        String content = msg.text();
        System.err.println("收到消息:"+ content);
        channels.writeAndFlush(new TextWebSocketFrame(content));
        System.err.println("寫出消息完成:"+content);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        channels.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

        String channelId = ctx.channel().id().asShortText();
        System.out.println("客戶端被移除,channelId為:" + channelId);
        channels.remove(ctx.channel());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        // 發(fā)生異常之后關(guān)閉連接(關(guān)閉channel),隨后從ChannelGroup中移除
        ctx.channel().close();
        channels.remove(ctx.channel());
    }

}

4.3 網(wǎng)頁客戶端實現(xiàn)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Netty視頻彈幕實現(xiàn) Author:Binhao Liu</title>
    <link rel="stylesheet" href="">
    <style type="text/css" media="screen">
        * {
            margin: 0px;
            padding: 0px
        }

        html, body {
            height: 100%
        }

        body {
            overflow: hidden;
            background-color: #FFF;
            text-align: center;
        }

        .flex-column {
            display: flex;
            flex-direction: column;
            justify-content: space-between;, align-items: center;
        }

        .flex-row {
            display: flex;
            flex-direction: row;
            justify-content: center;
            align-items: center;
        }

        .wrap {
            overflow: hidden;
            width: 70%;
            height: 600px;
            margin: 100px auto;
            padding: 20px;
            background-color: transparent;
            box-shadow: 0 0 9px #222;
            border-radius: 20px;
        }

        .wrap .box {
            position: relative;
            width: 100%;
            height: 90%;
            background-color: #000000;
            border-radius: 10px
        }

        .wrap .box span {
            position: absolute;
            top: 10px;
            left: 20px;
            display: block;
            padding: 10px;
            color: #336688
        }

        .wrap .send {
            display: flex;
            width: 100%;
            height: 10%;
            background-color: #000000;
            border-radius: 8px
        }

        .wrap .send input {
            width: 40%;
            height: 60%;
            border: 0;
            outline: 0;
            border-radius: 5px 0px 0px 5px;
            box-shadow: 0px 0px 5px #d9d9d9;
            text-indent: 1em
        }

        .wrap .send .send-btn {
            width: 100px;
            height: 60%;
            background-color: #fe943b;
            color: #FFF;
            text-align: center;
            border-radius: 0px 5px 5px 0px;
            line-height: 30px;
            cursor: pointer;
        }

        .wrap .send .send-btn:hover {
            background-color: #4cacdc
        }
    </style>
</head>
<script>
    var ws = new WebSocket("ws://localhost:9123/lbh");

    ws.onopen = function () {
        // Web Socket 已連接上,使用 send() 方法發(fā)送數(shù)據(jù)
        alert("數(shù)據(jù)發(fā)送中...");
    };
    ws.onmessage = function (e) {
        console.log("接受到消息:"+e.data);
        createEle(e.data);
    };
    ws.onclose = function () {
        // 關(guān)閉 websocket
        alert("連接已關(guān)閉...");
    };
    function sendMsg(msg) {
        ws.send(msg)
    }


</script>
<body>
<div class="wrap flex-column">
    <div class="box">
        <video src="shape.mp4" width="100%" height="100%" controls autoplay></video>
    </div>
    <div class="send flex-row">

        <input type="text" class="con" placeholder="彈幕發(fā)送[]~(^v^)~*"/>

        <div class="send-btn" onclick="javascript:sendMsg(document.querySelector('.con').value)">發(fā)送</div>
    </div>
</div>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" type="text/javascript"></script>
<script>
    //1.獲取元素
    var oBox = document.querySelector('.box');   //獲取.box元素
    var cW = oBox.offsetWidth;   //獲取box的寬度
    var cH = oBox.offsetHeight;   //獲取box的高度
    function createEle(txt) {
        //動態(tài)生成span標簽
        var oMessage = document.createElement('span');   //創(chuàng)建標簽
        oMessage.innerHTML = txt;   //接收參數(shù)txt并且生成替換內(nèi)容
        oMessage.style.left = cW + 'px';  //初始化生成位置x
        oBox.appendChild(oMessage);   //把標簽塞到oBox里面
        roll.call(oMessage, {
            //call改變函數(shù)內(nèi)部this的指向
            timing: ['linear', 'ease-out'][~~(Math.random() * 2)],
            color: '#' + (~~(Math.random() * (1 << 24))).toString(16),
            top: random(0, cH),
            fontSize: random(16, 32)
        });
    }

    function roll(opt) {
        //彈幕滾動
        //如果對象中不存在timing 初始化
        opt.timing = opt.timing || 'linear';
        opt.color = opt.color || '#fff';
        opt.top = opt.top || 0;
        opt.fontSize = opt.fontSize || 16;
        this._left = parseInt(this.offsetLeft);   //獲取當前l(fā)eft的值
        this.style.color = opt.color;   //初始化顏色
        this.style.top = opt.top + 'px';
        this.style.fontSize = opt.fontSize + 'px';
        this.timer = setInterval(function () {
            if (this._left <= 100) {
                clearInterval(this.timer);   //終止定時器
                this.parentNode.removeChild(this);
                return;   //終止函數(shù)
            }
            switch (opt.timing) {
                case 'linear':   //如果勻速
                    this._left += -2;
                    break;
                case 'ease-out':   //
                    this._left += (0 - this._left) * .01;
                    break;
            }
            this.style.left = this._left + 'px';
        }.bind(this), 1000 / 60);
    }

    function random(start, end) {
        //隨機數(shù)封裝
        return start + ~~(Math.random() * (end - start));
    }

    var aLi = document.querySelectorAll('li');   //10

    function forEach(ele, cb) {
        for (var i = 0, len = aLi.length; i < len; i++) {
            cb && cb(ele[i], i);
        }
    }

    forEach(aLi, function (ele, i) {
        ele.style.left = i * 100 + 'px';
    });
    //產(chǎn)生閉包
    var obj = {
        num: 1,
        add: function () {
            this.num++;   //obj.num = 2;
            (function () {
                console.log(this.num);
            })
        }
    };
    obj.add();//window

</script>
</body>
</html>

       這樣一個實時的視頻彈幕功能就完成啦,是不是很簡單,各位小伙伴快來試試吧。

5 小結(jié)

       上班擼代碼,下班繼續(xù)擼代碼寫博客,這個還是很簡單,筆者寫這個的時候一會兒就寫完了,不過這也得益于筆者很久以前就寫過netty的服務,對于Http,Tcp之類協(xié)議也比較熟悉,只有前端會有些難度,問下度娘,也很快能做完,在此分享出來與諸君分享。

到此這篇關(guān)于Java基于websocket協(xié)議與netty實時視頻彈幕交互實現(xiàn)的文章就介紹到這了,更多相關(guān)Java websocket與netty彈幕交互內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot啟動異常Exception in thread “main“ java.lang.UnsupportedClassVersionError

    SpringBoot啟動異常Exception in thread “main“ 

    本文主要介紹了SpringBoot啟動異常Exception in thread “main“ java.lang.UnsupportedClassVersionError,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-07-07
  • IDEA使用Tomcat運行web項目教程分享

    IDEA使用Tomcat運行web項目教程分享

    在非Spring Boot項目中運行Nacos示例,需要手動配置Tomcat容器,本文介紹了如何在IDEA中配置Tomcat,并詳細解決了配置過程中可能遇到的異常情況,步驟包括修改IDEA項目結(jié)構(gòu)、添加Web模塊、配置Artifacts和Tomcat Server
    2024-10-10
  • 基于Java解析國密數(shù)字證書的操作方法

    基于Java解析國密數(shù)字證書的操作方法

    在Java環(huán)境中解析使用國密算法(如SM3WITHSM2)的數(shù)字證書可能遇到挑戰(zhàn),使用BouncyCastle加密庫可以解決Java標準庫無法識別國密算法橢圓曲線的問題,成功解析國密數(shù)字證書,添加BouncyCastle依賴并修改代碼,使其支持國密算法,即可解析采用SM3WITHSM2算法的數(shù)字證書
    2024-09-09
  • Java阻塞隊列四組API介紹(小結(jié))

    Java阻塞隊列四組API介紹(小結(jié))

    這篇文章主要介紹了Java阻塞隊列四組API介紹,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • 詳解springboot啟動時是如何加載配置文件application.yml文件

    詳解springboot啟動時是如何加載配置文件application.yml文件

    這篇文章主要介紹了詳解springboot啟動時是如何加載配置文件application.yml文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • mybatis-flex實現(xiàn)多數(shù)據(jù)源操作

    mybatis-flex實現(xiàn)多數(shù)據(jù)源操作

    MyBaits-Flex內(nèi)置了功能完善的多數(shù)據(jù)源支持,本文主要介紹了mybatis-flex實現(xiàn)多數(shù)據(jù)源操作,具有一定的參考價值,感興趣的可以了解一下
    2024-06-06
  • 聊聊Spring——AOP詳解(AOP概覽)

    聊聊Spring——AOP詳解(AOP概覽)

    這篇文章主要介紹了Spring——AOP詳解(AOP概覽),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • SpringBoot使用validation進行自參數(shù)校驗的方法

    SpringBoot使用validation進行自參數(shù)校驗的方法

    在SpringBoot項目中,利用validation依賴可以通過注解方式校驗數(shù)據(jù)庫交互參數(shù),提高代碼可讀性和維護性,此方法避免了硬編碼校驗規(guī)則,方便后期規(guī)則變更,本文給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-09-09
  • Java編程獲取文本框的內(nèi)容實例解析

    Java編程獲取文本框的內(nèi)容實例解析

    這篇文章主要介紹了Java編程獲取文本框的值實例解析,將輸入的值保存在一個指定的 txt文件之中,具有一定的參考價值,需要的朋友可以了解。
    2017-09-09
  • IDEA中如何使用注解Test

    IDEA中如何使用注解Test

    這篇文章主要介紹了IDEA中如何使用注解Test問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05

最新評論

工布江达县| 湟源县| 宁明县| 华容县| 理塘县| 晋中市| 昌黎县| 禄丰县| 大名县| 景泰县| 昌江| 琼结县| 梁河县| 连江县| 龙岩市| 临朐县| 诸暨市| 台中县| 马尔康县| 独山县| 韶关市| 蓬莱市| 青冈县| 乌兰察布市| 邹城市| 怀安县| 石台县| 静宁县| 平江县| 普格县| 嵊州市| 四会市| 敖汉旗| 大安市| 阳朔县| 安吉县| 青田县| 尉氏县| 巴楚县| 弥渡县| 西安市|