欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

摘要

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

1 技術(shù)選型

1.1 netty

       官方對(duì)于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ū)動(dòng)網(wǎng)絡(luò)框架,可做各種協(xié)議服務(wù)端,并且支持了FTP,SMTP,HTTP等很多協(xié)議,并且性能,穩(wěn)定性,靈活性都很棒。

netty整體架構(gòu)
    

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

1.2 WebSocket

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

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

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

2 實(shí)現(xiàn)思路

2.1 服務(wù)架構(gòu)

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

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

2.2 傳輸流程

3 實(shí)現(xiàn)效果

3.1 視頻展示

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

視頻直播彈幕示例

4 代碼實(shí)現(xiàn)

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

一個(gè)maven項(xiàng)目,將代碼放一個(gè)包下就行。

在這里插入圖片描述

4.2 Java服務(wù)端

       Java服務(wù)端代碼,總共三個(gè)類,Server,Initailizer和 Handler。

4.2.1 先做一個(gè)netty nio的服務(wù)端:

       一個(gè)nio的服務(wù),開啟一個(gè)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 服務(wù)端的具體處理邏輯

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());
    }
}


[^

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

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)頁客戶端實(shí)現(xiàn)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Netty視頻彈幕實(shí)現(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) {
        //動(dòng)態(tài)生成span標(biāo)簽
        var oMessage = document.createElement('span');   //創(chuàng)建標(biāo)簽
        oMessage.innerHTML = txt;   //接收參數(shù)txt并且生成替換內(nèi)容
        oMessage.style.left = cW + 'px';  //初始化生成位置x
        oBox.appendChild(oMessage);   //把標(biāo)簽塞到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) {
        //彈幕滾動(dòng)
        //如果對(duì)象中不存在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);   //獲取當(dāng)前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);   //終止定時(shí)器
                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) {
        //隨機(jī)數(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>

       這樣一個(gè)實(shí)時(shí)的視頻彈幕功能就完成啦,是不是很簡(jiǎn)單,各位小伙伴快來試試吧。

5 小結(jié)

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

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

相關(guān)文章

  • MyBatis的CRUD中的不同參數(shù)綁定查詢實(shí)現(xiàn)

    MyBatis的CRUD中的不同參數(shù)綁定查詢實(shí)現(xiàn)

    本文主要介紹了MyBatis的CRUD中的不同參數(shù)綁定查詢實(shí)現(xiàn),主要包括單個(gè)參數(shù)傳遞綁定,序號(hào)參數(shù)傳遞綁定,注解參數(shù)傳遞綁定,pojo(對(duì)象)參數(shù)傳遞綁定,map參數(shù)傳遞綁定這幾種類型,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • spring-session簡(jiǎn)介及實(shí)現(xiàn)原理源碼分析

    spring-session簡(jiǎn)介及實(shí)現(xiàn)原理源碼分析

    這篇文章主要介紹了spring-session簡(jiǎn)介及實(shí)現(xiàn)原理源碼分析,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • 基于Java中字符串indexof() 的使用方法

    基于Java中字符串indexof() 的使用方法

    今天小編就為大家分享一篇基于Java中字符串indexof() 的使用方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • SpringBoot配置SwaggerUI訪問404錯(cuò)誤的解決方法

    SpringBoot配置SwaggerUI訪問404錯(cuò)誤的解決方法

    這篇文章主要為大家詳細(xì)介紹了SpringBoot配置SwaggerUI訪問404錯(cuò)誤的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Java中LinkedHashMap的實(shí)現(xiàn)詳解

    Java中LinkedHashMap的實(shí)現(xiàn)詳解

    LinkedHashMap是Java中的一個(gè)Map容器,它繼承自HashMap,并且還可以對(duì)元素進(jìn)行有序存儲(chǔ),本文將介紹LinkedHashMap的實(shí)現(xiàn)原理以及使用方法,并且提供相應(yīng)的測(cè)試用例和全文小結(jié),需要的可以參考下
    2023-09-09
  • Java并發(fā)編程之死鎖相關(guān)知識(shí)整理

    Java并發(fā)編程之死鎖相關(guān)知識(shí)整理

    前篇文章在講解線程安全的時(shí)候,有提到過為了保證每個(gè)線程都能正常執(zhí)行共享資源操作,Java引入了鎖機(jī)制,雖然這樣使多線程改善了系統(tǒng)的處理能力,然而也帶來了新的問題,其中之一:死鎖,需要的朋友可以參考下
    2021-06-06
  • 微信小程序與AspNetCore SignalR聊天實(shí)例代碼

    微信小程序與AspNetCore SignalR聊天實(shí)例代碼

    這篇文章主要介紹了微信小程序與AspNetCore SignalR聊天實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • Java生成表格圖片的實(shí)例代碼

    Java生成表格圖片的實(shí)例代碼

    這篇文章主要介紹了Java生成表格圖片的實(shí)例代碼,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-09-09
  • springboot排除某些自動(dòng)配置的操作方法

    springboot排除某些自動(dòng)配置的操作方法

    Spring Boot 提供的自動(dòng)配置非常強(qiáng)大,某些情況下,自動(dòng)配置的功能可能不符合我們的需求,需要我們自定義配置,這個(gè)時(shí)候就需要排除/禁用Spring Boot 某些類的自動(dòng)化配置了,本文給大家介紹springboot排除某些自動(dòng)配置的方法,感興趣的朋友一起看看吧
    2023-08-08
  • 基于ArrayList常用方法的源碼全面解析

    基于ArrayList常用方法的源碼全面解析

    下面小編就為大家?guī)硪黄贏rrayList常用方法的源碼全面解析。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06

最新評(píng)論