Netty开发弹幕系统

发布时间:2024年01月13日

用Netty+websocket实现简单的web弹幕系统

服务端代码

1. pom依赖

        <!-- Netty -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.66.Final</version>
        </dependency>

        <!-- Netty WebSocket -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-transport-native-epoll</artifactId> <!-- Use the appropriate transport for your system -->
            <version>4.1.66.Final</version>
            <scope>provided</scope>
        </dependency>

2. NettyServer

public class DanmuServer {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new HttpServerCodec());
                            p.addLast(new HttpObjectAggregator(65536));
                            p.addLast(new WebSocketServerProtocolHandler("/websocket"));
                            p.addLast(new DanmuHandler());
                        }
                    });

            Channel ch = b.bind(8080).sync().channel();
            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

3. 业务Handler?

public class DanmuHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    private static final CopyOnWriteArrayList<Channel> channels = new CopyOnWriteArrayList<>();

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        channels.add(incoming);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        channels.remove(incoming);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 处理收到的消息
        String receivedMessage = msg.text();
        System.out.println("Received message: " + receivedMessage);
        String clientId = ctx.channel().id().asShortText();
        // 广播消息给所有连接的客户端
        for (Channel channel : channels) {
            channel.writeAndFlush(new TextWebSocketFrame(clientId + ":" + receivedMessage));
        }
    }
}

客户端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Danmu System</title>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #danmuContainer {
            height: calc(100vh - 40px); /* 调整弹幕容器高度,让底部空出40px给输入框 */
            overflow: hidden;
            position: relative;
        }

        .danmuMessage {
            position: absolute;
            white-space: nowrap;
            left: 0;
        }

        #inputContainer {
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            background-color: #f0f0f0;
            padding: 10px;
            box-sizing: border-box;
        }

        #messageInput {
            width: calc(100% - 20px);
            padding: 5px;
        }
    </style>
</head>
<body>
<div id="danmuContainer"></div>
<div id="inputContainer">
    <input type="text" id="messageInput" placeholder="Type your message and press Enter">
</div>

<script>
    const danmuContainer = document.getElementById("danmuContainer");
    const messageInput = document.getElementById("messageInput");

    let socket;
    function connectWebSocket() {
        socket = new WebSocket("ws://localhost:8080/websocket");

        socket.onmessage = function (event) {
            const receivedMessage = event.data;
            displayDanmu(receivedMessage);
        };

        // 关闭连接时, 重连服务器
        socket.onclose = function (event) {
            console.error("WebSocket closed. Reconnecting...");
            setTimeout(connectWebSocket, 1000); // 1秒后尝试重新连接
        };
    }

    function sendMessage() {
        const message = messageInput.value.trim();
        if (message !== "") {
            socket.send(message);
            messageInput.value = "";
        }
    }

    function displayDanmu(message) {
        const danmuMessage = document.createElement("div");
        danmuMessage.className = "danmuMessage";
        danmuMessage.textContent = message;

        // 设置初始位置,确保弹幕元素在屏幕最左侧开始滑动
        danmuMessage.style.top = `${Math.floor(Math.random() * (danmuContainer.clientHeight - danmuMessage.clientHeight - 10))}px`;
        danmuMessage.style.left = "0";

        danmuContainer.appendChild(danmuMessage);

        // 使用CSS动画实现滑动效果
        danmuMessage.style.transition = `left 10s linear`; // 调整持续时间和缓动函数

        // 设置动画结束后,移除弹幕元素
        danmuMessage.addEventListener("transitionend", function() {
            danmuContainer.removeChild(danmuMessage);
        });

        // 设置滑动终点位置
        danmuMessage.style.left = `${danmuContainer.clientWidth}px`;
    }

    // 监听鼠标回车事件, 回车发送消息
    messageInput.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
            sendMessage();
        }
    });

    // 建立连接
    connectWebSocket();
</script>
</body>
</html>

效果展示:浏览器访问html

文章来源:https://blog.csdn.net/weixin_64027360/article/details/135564057
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。