激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java基于websocket協議與netty實時視頻彈幕交互實現

Java基于websocket協議與netty實時視頻彈幕交互實現

2021-12-15 10:58興趣使然的程序猿 Java教程

本文主要介紹了Java基于websocket協議與netty實時視頻彈幕交互實現,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

 

摘要

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

 

1 技術選型

 

1.1 netty

       官方對于netty的描述:netty官網.

       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

       主要關鍵詞描述:netty是異步事件驅動網絡框架,可做各種協議服務端,并且支持了FTP,SMTP,HTTP等很多協議,并且性能,穩定性,靈活性都很棒。

Java基于websocket協議與netty實時視頻彈幕交互實現
    

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

 

1.2 WebSocket

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

 

1.3 為什么做這樣的技術選型。

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

 

2 實現思路

 

2.1 服務架構

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

Java基于websocket協議與netty實時視頻彈幕交互實現

2.2 傳輸流程

Java基于websocket協議與netty實時視頻彈幕交互實現

 

3 實現效果

 

3.1 視頻展示

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

視頻直播彈幕示例

 

4 代碼實現

 

4.1 項目結構

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

Java基于websocket協議與netty實時視頻彈幕交互實現

 

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();
        // 發生異常之后關閉連接(關閉channel),隨后從ChannelGroup中移除
        ctx.channel().close();
        channels.remove(ctx.channel());
    }

}

 

4.3 網頁客戶端實現

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Netty視頻彈幕實現 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() 方法發送數據
        alert("數據發送中...");
    };
    ws.onmessage = function (e) {
        console.log("接受到消息:"+e.data);
        createEle(e.data);
    };
    ws.onclose = function () {
        // 關閉 websocket
        alert("連接已關閉...");
    };
    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="彈幕發送[]~(^v^)~*"/>

        <div class="send-btn" onclick="javascript:sendMsg(document.querySelector(".con").value)">發送</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) {
        //動態生成span標簽
        var oMessage = document.createElement("span");   //創建標簽
        oMessage.innerHTML = txt;   //接收參數txt并且生成替換內容
        oMessage.style.left = cW + "px";  //初始化生成位置x
        oBox.appendChild(oMessage);   //把標簽塞到oBox里面
        roll.call(oMessage, {
            //call改變函數內部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);   //獲取當前left的值
        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;   //終止函數
            }
            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) {
        //隨機數封裝
        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";
    });
    //產生閉包
    var obj = {
        num: 1,
        add: function () {
            this.num++;   //obj.num = 2;
            (function () {
                console.log(this.num);
            })
        }
    };
    obj.add();//window

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

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

 

5 小結

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

到此這篇關于Java基于websocket協議與netty實時視頻彈幕交互實現的文章就介紹到這了,更多相關Java websocket與netty彈幕交互內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://binhao.blog.csdn.net/article/details/112631642

延伸 · 閱讀

精彩推薦
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 国产精品视频免费在线观看 | 99视频在线观看视频 | 国产精品午夜在线观看 | 男女无套免费视频 | 天海翼四虎精品正在播放 | 伊人yinren22综合网色 | 51国产偷自视频区视频小蝌蚪 | 一区二区三区在线视频观看58 | 国产 一区 精品 | 欧美日韩免费在线观看视频 | 日韩在线高清 | www.91成人| 精品一区二区三区在线观看国产 | 国产精品久久久久久久久久电影 | 成人永久免费 | 久久久一区二区三区视频 | 欧美日本91精品久久久久 | 免费啪啪 | 哪里可以看免费的av | 在线看小早川怜子av | 毛片免费在线播放 | 亚洲精品av在线 | 亚洲天堂欧美在线 | 免费黄色一级网站 | 久久久久久久久久久久免费 | 免费a视频在线观看 | 免费视频www在线观看 | 亚洲第一成人在线视频 | 日韩精品羞羞答答 | 中文字幕国 | 黄色免费视频在线 | 亚洲网站免费观看 | 国产精品免费小视频 | 毛片免费在线观看视频 | av免费在线观看国产 | 偿还的影视高清在线观看 | 国产美女爽到喷白浆的 | 欧美 日韩 亚洲 中文 | 成人免费在线观看视频 | 国产成人综合在线 | 欧美一级片免费在线观看 |