使用Netty构建Java多人聊天室

这篇文章将指导您使用Netty构建一个简单的Java多人聊天室。我们将涵盖服务器和客户端的实现,以及用户登录、聊天室列表、消息发送和私聊功能。

一、服务器端

服务器端代码使用Netty框架处理网络连接和数据传输。javaimport io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;import io.netty.util.concurrent.DefaultEventExecutorGroup;import io.netty.util.concurrent.EventExecutorGroup;

import java.util.concurrent.ConcurrentHashMap;

public class MultiChatServer { private final int port; private final ConcurrentHashMap<String, ChannelHandlerContext> userMap = new ConcurrentHashMap<>(); private final EventExecutorGroup group = new DefaultEventExecutorGroup(16);

public MultiChatServer(int port) {        this.port = port;    }

public void run() throws Exception {        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                        public void initChannel(SocketChannel ch) throws Exception {                            ch.pipeline().addLast(new StringDecoder(), new StringEncoder(), new MultiChatServerHandler(userMap, group));                        }                    })                    .option(ChannelOption.SO_BACKLOG, 128)                    .childOption(ChannelOption.SO_KEEPALIVE, true);

        ChannelFuture f = b.bind(port).sync();            f.channel().closeFuture().sync();        } finally {            workerGroup.shutdownGracefully();            bossGroup.shutdownGracefully();        }    }

public static void main(String[] args) throws Exception {        int port = 8080;        new MultiChatServer(port).run();    }}

二、MultiChatServerHandler类

该类处理服务器端的消息逻辑,包括用户登录、聊天室列表、消息发送和私聊功能。javaimport io.netty.channel.ChannelFuture;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.util.concurrent.EventExecutorGroup;

import java.util.Map;import java.util.concurrent.ConcurrentHashMap;

public class MultiChatServerHandler extends SimpleChannelInboundHandler { private final ConcurrentHashMap<String, ChannelHandlerContext> userMap; private final EventExecutorGroup group;

public MultiChatServerHandler(ConcurrentHashMap<String, ChannelHandlerContext> userMap, EventExecutorGroup group) {        this.userMap = userMap;        this.group = group;    }

@Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        System.out.println('Client connected: ' + ctx.channel().remoteAddress());    }

@Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        System.out.println('Client disconnected: ' + ctx.channel().remoteAddress());        userMap.values().remove(ctx);    }

@Override    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {        String[] tokens = msg.split('\|');        String command = tokens[0];

    switch (command) {            case 'LOGIN':                handleLogin(ctx, tokens[1], tokens[2]);                break;            case 'LIST':                handleList(ctx);                break;            case 'MSG':                handleMessage(ctx, tokens[1], tokens[2]);                break;            case 'PRIVATE':                handlePrivateMessage(ctx, tokens[1], tokens[2]);                break;            default:                ctx.writeAndFlush('Invalid command: ' + command);                break;        }    }

private void handleLogin(ChannelHandlerContext ctx, String username, String password) {        if (userMap.containsKey(username)) {            ctx.writeAndFlush('User already logged in: ' + username);        } else {            userMap.put(username, ctx);            ctx.writeAndFlush('Login successful: ' + username);        }    }

private void handleList(ChannelHandlerContext ctx) {        StringBuilder sb = new StringBuilder();        sb.append('Available chat rooms:

'); for (Map.Entry<String, ChannelHandlerContext> entry : userMap.entrySet()) { sb.append(entry.getKey()).append(' '); } ctx.writeAndFlush(sb.toString()); }

private void handleMessage(ChannelHandlerContext ctx, String roomName, String message) {        StringBuilder sb = new StringBuilder();        sb.append('[').append(ctx.channel().remoteAddress()).append('] ').append(message).append('

'); for (Map.Entry<String, ChannelHandlerContext> entry : userMap.entrySet()) { if (entry.getKey().equals(roomName)) { entry.getValue().writeAndFlush(sb.toString()); } } }

private void handlePrivateMessage(ChannelHandlerContext ctx, String recipient, String message) {        StringBuilder sb = new StringBuilder();        sb.append('[').append(ctx.channel().remoteAddress()).append('] (private) ').append(message).append('

'); if (userMap.containsKey(recipient)) { userMap.get(recipient).writeAndFlush(sb.toString()); } else { ctx.writeAndFlush('User not found: ' + recipient); } }

@Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }}

三、客户端

客户端代码连接到服务器并发送/接收消息。javaimport io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;

import java.io.BufferedReader;import java.io.InputStreamReader;

public class MultiChatClient { private final String host; private final int port; private ChannelHandlerContext ctx;

public MultiChatClient(String host, int port) {        this.host = host;        this.port = port;    }

public void run() throws Exception {        EventLoopGroup group = new NioEventLoopGroup();        try {            Bootstrap b = new Bootstrap();            b.group(group)                    .channel(NioSocketChannel.class)                    .option(ChannelOption.SO_KEEPALIVE, true)                    .handler(new ChannelInitializer<SocketChannel>() {                        @Override                        public void initChannel(SocketChannel ch) throws Exception {                            ch.pipeline().addLast(new StringDecoder(), new StringEncoder(), new MultiChatClientHandler());                        }                    });

        ChannelFuture f = b.connect(host, port).sync();            ctx = f.channel().pipeline().context(MultiChatClientHandler.class);            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));            while (true) {                String line = in.readLine();                if (line == null) {                    break;                }                ctx.writeAndFlush(line + '

'); } } finally { group.shutdownGracefully(); } }

public static void main(String[] args) throws Exception {        String host = 'localhost';        int port = 8080;        new MultiChatClient(host, port).run();    }}

四、MultiChatClientHandler类

该类处理客户端的消息逻辑,包括接收服务器端的消息和向服务器端发送消息。javaimport io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;

public class MultiChatClientHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); }

@Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }}

编译和运行

  1. 编译代码并打成jar包

    在命令行中进入项目根目录,执行以下命令:

    bash mvn clean package

    这将会编译项目并打成jar包,生成的jar包位于target目录下。

  2. 启动服务器端

    在命令行中进入jar包所在目录,执行以下命令:

    bash java -jar MultiChatServer.jar

    这将会启动服务器端,并监听8080端口。如果一切正常,控制台会输出'Server started: localhost:8080'。

  3. 启动客户端

    在命令行中进入jar包所在目录,执行以下命令:

    bash java -jar MultiChatClient.jar

    这将会启动客户端,并连接到服务器端。如果一切正常,客户端会等待用户输入消息。

客户端使用说明

客户端启动后,会等待用户输入消息。用户可以输入以下指令:

  • LOGIN|username|password:登录到聊天室,其中username和password分别表示用户名和密码。- LIST:列出当前可用的聊天室。- MSG|roomName|message:向指定聊天室发送消息,其中roomName表示聊天室名称,message表示要发送的消息。- PRIVATE|recipient|message:向指定用户发送私聊消息,其中recipient表示接收者用户名,message表示要发送的消息。

示例:

  • 登录到聊天室:

    LOGIN|alice|password

  • 向聊天室发送消息:

    MSG|room1|hello world!

  • 向其他用户发送私聊消息:

    PRIVATE|bob|hi there!

希望这篇文章能够帮助您使用Netty构建自己的Java多人聊天室!

使用Netty构建Java多人聊天室

原文地址: http://www.cveoy.top/t/topic/f10a 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录