Build a Real-Time Video Bullet Chat with Netty and WebSocket

This article walks through creating a real‑time video bullet‑chat system using Netty as the backend server and WebSocket for bidirectional communication, covering technology selection, architecture design, data flow, and detailed Java and HTML/JavaScript code to implement the interactive feature.

Programmer DD
Programmer DD
Programmer DD
Build a Real-Time Video Bullet Chat with Netty and WebSocket

In 2021, with the rise of bullet‑screen interactions across video platforms, the author implemented a real‑time video bullet‑chat feature to enhance the experience of video playback, live streaming, PPT presentations, and lotteries.

1 Technology Selection

1.1 Netty

Netty is an asynchronous event‑driven network framework that supports many protocols such as FTP, SMTP, HTTP, and offers excellent performance, stability, and flexibility.

Netty’s architecture consists of three parts:

Core layer with zero‑copy, consistent interfaces, and extensible event model.

Transport media such as Socket, Datagram, Pipe, and HTTP Tunnel.

Supported protocols including HTTP/WebSocket, SSL, large files, compression, text, binary, and Google Protobuf.

1.2 WebSocket

WebSocket is a full‑duplex communication protocol over a single TCP connection, standardized as RFC 6455 in 2011 and supported by both IETF and W3C.

It simplifies data exchange between client and server, allowing the server to push data actively. After a single handshake, a persistent bidirectional channel is established.

1.3 Why Choose This Stack

Real‑time interaction requires bidirectional data transmission, which WebSocket provides.

Netty already supports the WebSocket protocol, making implementation straightforward.

2 Implementation Idea

2.1 Service Architecture

All clients maintain a bidirectional channel with the server.

2.2 Transmission Process

3 Result

3.1 Video Display

Below is the effect of the bullet‑chat overlay on a video.

4 Code Implementation

4.1 Project Structure

A Maven project containing all source files under a single package.

4.2 Java Server

The server consists of three classes: BulletChatServer, BulletChatInitializer, and BulletChatHandler.

4.2.1 Netty NIO Server

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;

public enum BulletChatServer {
    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) {
        // start server
    }
}

4.2.2 Initializer

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;

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

4.2.3 Handler

import io.netty.channel.Channel;
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.GlobalEventExecutor;

public class BulletChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    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("Received: " + content);
        channels.writeAndFlush(new TextWebSocketFrame(content));
        System.err.println("Sent: " + 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("Client removed, channelId: " + channelId);
        channels.remove(ctx.channel());
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.channel().close();
        channels.remove(ctx.channel());
    }
}

4.3 Web Client

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>Netty Video Bullet Chat</title>
    <style>
        /* basic styles omitted for brevity */
    </style>
</head>
<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="Enter bullet chat..."/>
            <div class="send-btn" onclick="sendMsg(document.querySelector('.con').value)">Send</div>
        </div>
    </div>
    <script>
        var ws = new WebSocket("ws://localhost:9123/lbh");
        ws.onopen = function(){ alert("Connecting..."); };
        ws.onmessage = function(e){ console.log("Received: " + e.data); createEle(e.data); };
        ws.onclose = function(){ alert("Connection closed"); };
        function sendMsg(msg){ ws.send(msg); }
        // functions createEle, roll, random, etc. omitted for brevity
    </script>
</body>
</html>

The real‑time video bullet‑chat feature is now complete and can be tested easily.

5 Summary

The author finished the implementation quickly thanks to prior experience with Netty and related protocols. The tutorial provides a straightforward way to add interactive bullet‑chat to video streams, and readers are encouraged to try it and ask questions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaNettyWebSocketBulletChat
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.