websocket+netty实时视频弹幕交互功能(Java版)

2021年了,还有不支持弹幕的视频网站吗,现在各种弹幕玩法层出不穷,抽奖,ppt都上弹幕玩法了,不整个弹幕都说不过去了,今天笔者就抽空做了一个实时视频弹幕交互功能的实现,不得不说这样的形式为看视频看直播,讲义PPT,抽奖等形式增加了许多乐趣。

1 技术选型

1.1 netty
官方对于netty的描述:

https://netty.io/

主要关键词描述:netty是异步事件驱动网络框架,可做各种协议服务端,并且支持了FTP,SMTP,HTTP等很多协议,并且性能,稳定性,灵活性都很棒。
websocket+netty实时视频弹幕交互功能(Java版)_第1张图片
可以看到netty整体架构上分了三个部分:

  • 以零拷贝,一致性接口,扩展事件模型的底层核心。
  • Socket,Datagram,Pipe,Http Tunnel作为传输媒介。
  • 传输支持的各种协议,HTTP&WebSocket,SSL,大文件,zlib/gzip压缩,文本,二进制,Google Protobuf等各种各种的传输形式。

1.2 WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

1.3 为什么做这样的技术选型。
由上述可知,实时直播交互作为互动式是一个双向数据传输过程。所以使用webSocket。
netty本身支持了webSocket协议的实现,让实现更加简单方便。

2 实现思路

2.1 服务架构
整体架构是所有客户端都和我的服务端开启一个双向通道的架构。
websocket+netty实时视频弹幕交互功能(Java版)_第2张图片
2.2 传输流程
websocket+netty实时视频弹幕交互功能(Java版)_第3张图片

3 实现效果

3.1 视频展示
先看看效果吧,是不是perfect,接下来就来看具体代码是怎么实现的吧。
websocket+netty实时视频弹幕交互功能(Java版)_第4张图片
图片视频直播弹幕示例

4 代码实现

4.1 项目结构
一个maven项目,将代码放一个包下就行。
websocket+netty实时视频弹幕交互功能(Java版)_第5张图片
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)[email protected]
 * @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)[email protected]
 *
 * @author liubinhao
 * @date 2021/1/14
 * ++++ ______                           ______             ______
 * +++/     /|                         /     /|           /     /|
 * +/_____/  |                       /_____/  |         /_____/  |
 * |     |   |                      |     |   |        |     |   |
 * |     |   |                      |     |   |________|     |   |
 * |     |   |                      |     |  /         |     |   |
 * |     |   |                      |     |/___________|     |   |
 * |     |   |___________________   |     |____________|     |   |
 * |     |  /                  / |  |     |   |        |     |   |
 * |     |/ _________________/  /   |     |  /         |     |  /
 * |_________________________|/b    |_____|/           |_____|/
 */

public class BulletChatInitializer extends ChannelInitializer {
    @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)[email protected]
 *
 * @author liubinhao
 * @date 2021/1/14
 * ++++ ______                           ______             ______
 * +++/     /|                         /     /|           /     /|
 * +/_____/  |                       /_____/  |         /_____/  |
 * |     |   |                      |     |   |        |     |   |
 * |     |   |                      |     |   |________|     |   |
 * |     |   |                      |     |  /         |     |   |
 * |     |   |                      |     |/___________|     |   |
 * |     |   |___________________   |     |____________|     |   |
 * |     |  /                  / |  |     |   |        |     |   |
 * |     |/ _________________/  /   |     |  /         |     |  /
 * |_________________________|/b    |_____|/           |_____|/
 */

public class BulletChatHandler  extends SimpleChannelInboundHandler {
    // 用于记录和管理所有客户端的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 网页客户端实现




    
    
    Netty视频弹幕实现 Author:Binhao Liu
    
    



发送

这样一个实时的视频弹幕功能就完成啦,是不是很简单,各位小伙伴快来试试吧。

5 小结

上班撸代码,下班继续撸代码写博客,这个还是很简单,笔者写这个的时候一会儿就写完了,不过这也得益于笔者很久以前就写过netty的服务,对于Http,Tcp之类协议也比较熟悉,只有前端会有些难度,问下度娘,也很快能做完,在此分享出来与诸君分享。

来源:binhao.blog.csdn.net/article/details/112631642

你可能感兴趣的:(java)