通过Java-Netty实现一个简单的HTML实时聊天——demo

发布时间 2023-11-29 16:06:27作者: 戴某人

demo效果

初步准备

想要实现聊天就需要用到WebSocket,他是专门用于http进行实时聊天的的协议。因为主要核心在后端开发,所以我去白嫖了个超级简单的界面(就是不会!)

我编写demo时系统环境为Win-11,Java-8

环境依赖

只需要一个netty即可实现需要的功能

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

其余依赖

如果你是一点不会可能会有几个函数会出现问题,那我也贴出来

<!--日志-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
</dependency>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<!--工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.20</version>
</dependency>

前端代码

就是一个简单的发送消息然后显示返回消息的界面,然后通过websocket来发送和接收,没有其他任何元素。

<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<head>
    <title>实时聊天</title>
</head>
<body>
<div id="chat">
    <div id="messages"></div>
    <input type="text" id="messageInput" placeholder="输入消息...">
    <button onclick="sendMessage()">发送</button>
</div>
<script>
    let ws;
    function connect() {
        //你的websocket
        ws = new WebSocket('ws://localhost:32001/daimouren');

        ws.onmessage = function(event) {
            let message = event.data;
            document.getElementById('messages').innerHTML += '<div>' + message + '</div>';
        };

        ws.onerror = function(event) {
            console.error("WebSocket error observed:", event);
        };
    }

    function sendMessage() {
        let message = document.getElementById('messageInput').value;
        ws.send(message);
        document.getElementById('messageInput').value = '';
    }

    window.onload = connect;
</script>
</body>
</html>

后端代码

后端代码也是非常简单,Netty已经对websocket实现进行了封装。此处就是简单的一个实现对应的功能的demo。核心的逻辑就是这些,之后改协议,绑定数据库等操作我就懒得写了。主要就是demo

package com.daimouren.netty.communicate;

import ch.qos.logback.core.util.TimeUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * HTTP页面实时交流功能(服务端)
 * @author daimouren
 * @since 2023/11/29
 */
@Slf4j
public class CommunicateServerDemo {

    private static final List<Channel> CONNECT_LIST = new ArrayList<>();

    public static void main(String[] args) {
        ServerBootstrap server = new ServerBootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();
        NioEventLoopGroup event = new NioEventLoopGroup();
        server.group(group,event); //监听事件处理
        server.channel(NioServerSocketChannel.class);
        server.childHandler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) {
                //打印的日志
                ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                //http协议处理器
                ch.pipeline().addLast(new HttpServerCodec());
                //半包黏包处理器,这个处理器专门用于http非常适合。请求为最大请求长度
                ch.pipeline().addLast(new HttpObjectAggregator(65536));
                //websocket的处理器。参数为请求路径
                ch.pipeline().addLast(new WebSocketServerProtocolHandler("/daimouren"));
                //websocket核心逻辑处理器
                ch.pipeline().addLast(new SimpleChannelInboundHandler<TextWebSocketFrame>() {
                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
                        //接收消息
                        String text = msg.text();
                        //创建发送消息体
                        TextWebSocketFrame backText = new TextWebSocketFrame((text));
                        //发送消息给除了自己的所有人
                        CONNECT_LIST.forEach(channel -> {
                            if (channel != ctx.channel()){
                                //发送消息
                                channel.writeAndFlush(backText);
                            }
                        });
                    }

                    @Override
                    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
                        //将连接添加到数组
                        CONNECT_LIST.add(ctx.channel());
                    }

                    @Override
                    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
                        //将连接提出数组
                        CONNECT_LIST.remove(ctx.channel());
                    }
                });
            }
        });
        try {
            # 端口绑定
            Channel channel = server.bind(32001).sync().channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            group.shutdownGracefully();
            event.shutdownGracefully();
        }
    }
}