Java基于websocket协议与netty实时视频弹幕交互实现

目录
  • 摘要
  • 1 技术选型
    • 1.1 netty
    • 1.2 WebSocket
    • 1.3 为什么做这样的技术选型。
  • 2 实现思路
    • 2.1 服务架构
  • 3 实现效果
    • 3.1 视频展示
  • 4 代码实现
    • 4.1 项目结构
    • 4.2 Java服务端
    • 4.3 网页客户端实现
  • 5 小结

摘要

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等很多协议,并且性能,稳定性,灵活性都很棒。

可以看到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 服务架构

整体架构是所有客户端都和我的服务端开启一个双向通道的架构。

2.2 传输流程

3 实现效果

3.1 视频展示

先看看效果吧,是不是perfect,接下来就来看具体代码是怎么实现的吧。

视频直播弹幕示例

4 代码实现

4.1 项目结构

一个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();
        // 发生异常之后关闭连接(关闭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弹幕交互内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot+WebSocket+Netty实现消息推送的示例代码

    上一篇文章讲了Netty的理论基础,这一篇讲一下Netty在项目中的应用场景之一:消息推送功能,可以满足给所有用户推送,也可以满足给指定某一个用户推送消息,创建的是SpringBoot项目,后台服务端使用Netty技术,前端页面使用WebSocket技术. 大概实现思路: 前端使用webSocket与服务端创建连接的时候,将用户ID传给服务端 服务端将用户ID与channel关联起来存储,同时将channel放入到channel组中 如果需要给所有用户发送消息,直接执行channel组的writ

  • Springboot+Netty+Websocket实现消息推送实例

    前言 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输. Netty框架的优势 1. API使用简单,开发门槛低:  2. 功能强大,预置了多种编解码功能,支持多种主流协议:  3. 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展:  4. 性能高,通过与其他业界主流的NIO框架对比,Netty的综

  • SpringBoot+Netty+WebSocket实现消息发送的示例代码

    一.导入Netty依赖 <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency> 二.搭建websocket服务器 @Component public class WebSocketServer { /** * 主线程池 */

  • Java基于websocket协议与netty实时视频弹幕交互实现

    目录 摘要 1 技术选型 1.1 netty 1.2 WebSocket 1.3 为什么做这样的技术选型. 2 实现思路 2.1 服务架构 3 实现效果 3.1 视频展示 4 代码实现 4.1 项目结构 4.2 Java服务端 4.3 网页客户端实现 5 小结 摘要 2021年了,还有不支持弹幕的视频网站吗,现在各种弹幕玩法层出不穷,抽奖,ppt都上弹幕玩法了,不整个弹幕都说不过去了,今天笔者就抽空做了一个实时视频弹幕交互功能的实现,不得不说这样的形式为看视频看直播,讲义PPT,抽奖等形式增加了

  • Java基于Tcp协议的socket编程实例

    本文实例讲述了Java基于Tcp协议的socket编程方法,分享给大家供大家参考.具体分析如下: 以下是一对一的通信编程实现,后续会继续学习一个服务器监听多个客户端的实现. 这里用到的主要步骤如下: 第一步:以特定端口(如4800)新建socket对象 第二步:以系统输入设备构造BufferedReader对象,该对象用于接收系统键盘输入的字符 第三步:以socket对象 得到输出流来构造PrintWriter 第四步:以socket对象得到输入流来构造相应的BufferedReader对象,该

  • Java 基于tcp协议实现文件上传

    服务端 package lesson02; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 服务端接收文件 */ public class TcpServerDemo2 { public static void main(String[] args) throws IOException { //1.创建服务 ServerSocket serverSocket = new ServerSo

  • Java 基于UDP协议实现消息发送

    发短信:不用连接,但需要知道对方的地址,客户端.服务端没有明确的界限,可以说没有客户端.服务端一说. 发送端 package lesson03; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * 发送端 */ public class UdpClientDemo1 { public static void main(String[] args) th

  • Java基于TCP协议socket网络编程的文件传送的实现

    先了解一下socket基本概念 socket也叫套接字: 是指在网路中不同主机上的应用进程之间,进行双向通信的端点的抽象. 简单理解就是: 两个主机之间要通信,就需要知道彼此的ip,端口号等信息,而一台主机这些信息的集合: 就可以理解为一个端点,即为套接字 双方通过套接字作为一种坐标,建立信息通道,形成连接(两点连接一条直线) 简单理解了套接字的概念后,来看看如何通过java socket编程来实现 两台主机文件的接收与发送: 代码如下: 发送方: import java.io.*; impor

  • Java基于TCP协议的Socket通信

    目录 简介 TCP简介 JAVA Socket简介 SocketImpl介绍 TCP 编程 构造ServerSocket 1.1 绑定端口 1.2 设定客户连接请求队列的长度 1.3 设定绑定的IP 地址 1.4 默认构造方法的作用 多线程示例 简介 TCP简介 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793定义.在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户

  • 基于java实现websocket协议过程详解

    最近了解了下websocket和socket这个东西,说不得不来说下为何要使用 WebSocket ,和为何不用http. 为何需要WebSocket ? HTTP 协议是一种无状态的.无连接的.单向的应用层协议.它采用了请求/响应模型.通信请求只能由客户端发起,服务端对请求做出应答处理. 这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息. 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦.大多数 Web 应用程序将通过频繁的异步JavaScr

  • Java基于UDP协议实现简单的聊天室程序

    最近比较闲,一直在抽空回顾一些Java方面的技术应用. 今天没什么事做,基于UDP协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对Java网络编程方面的一个简单回忆. 先看一下效果: 实现的效果可以说是非常非常简单,但还是可以简单的看到一个实现原理.  "聊天室001"的用户,小红和小绿相互聊了两句,"聊天室002"的小黑无人理会,在一旁寂寞着. 看一下代码实现: 1.首先是消息服务器的实现,功能很简单: •将客户端的信息(进入了哪一

  • java基于TCP协议实现聊天程序

    JAVA程序设计之基于TCP协议的socket聊天程序 ,供大家参考,具体内容如下 一.程序实现的功能 1.进入客户端界面 2.创建昵称 3.群发信息 4.@私聊 5.下线通知 6.在线人数统计 二.整体架构图 三.简单介绍 本程序实现了基于TCP通信的聊天程序: 1 服务器端: 服务器端继承JFrame框架,添加组件.创建服务器端的socket,起一个线程池,每接收到一个客户端的连接,分配给其一个线程处理与客户端的通信,将每个客户端的昵称和服务器分配给其的输出流存储到哈希表中.通过检索哈希表中

  • Java基于UDP协议的聊天室功能

    UDP简述 UDP(User Datagram Protocol)协议是Internet 协议集支持的一个无连接的传输协议,中文名为用户数据报协议.它为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法. Internet 的传输层有两个主要协议,互为补充.无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情.面向连接的是TCP,该协议几乎做了所有的事情.“when in trouble , use TCP”. 那

随机推荐