基于Netty的Java多人聊天室实现
基于Netty的Java多人聊天室实现
本篇博客将详细介绍如何使用Netty构建一个基于Java的多人聊天室。我们将涵盖服务器端和客户端的实现,以及用户登录、聊天室列表、消息发送和私聊功能。
一、服务器端实现
1. 服务器端主类 MultiChatServerjavaimport 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; if (args.length > 0) { port = Integer.parseInt(args[0]); } new MultiChatServer(port).run(); }}
该类负责启动服务器并监听指定端口的连接请求。
- 使用
NioEventLoopGroup创建两个线程池,分别用于处理连接请求和处理IO事件。- 使用ServerBootstrap配置服务器,包括线程池、通道类型、处理器等。- 在ChannelInitializer中添加StringDecoder和StringEncoder用于字符串编解码,以及MultiChatServerHandler用于处理业务逻辑。
2. 消息处理器 MultiChatServerHandlerjavaimport 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
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 users:
'); for (Map.Entry<String, ChannelHandlerContext> entry : userMap.entrySet()) { sb.append(entry.getKey()).append(' '); } ctx.writeAndFlush(sb.toString()); }
private void handleMessage(ChannelHandlerContext ctx, String recipient, String message) { group.execute(() -> { StringBuilder sb = new StringBuilder(); sb.append('[').append(ctx.channel().remoteAddress()).append('] ').append(message).append('
'); if (userMap.containsKey(recipient)) { userMap.get(recipient).writeAndFlush(sb.toString()); } else { ctx.writeAndFlush('User not found: ' + recipient); } }); }
private void handlePrivateMessage(ChannelHandlerContext ctx, String recipient, String message) { handleMessage(ctx, recipient, message); // 私聊和群聊逻辑相同,只是recipient不同 }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}
该类负责处理客户端发送的各种消息,包括登录、获取聊天室列表、发送消息和发送私聊消息。
channelRead0方法接收客户端发送的字符串消息,并根据消息类型进行处理。-handleLogin方法处理用户登录请求,将用户名和对应的ChannelHandlerContext存入userMap中。-handleList方法向客户端发送当前在线用户的列表。-handleMessage方法处理发送消息请求,将消息转发给指定的用户。-handlePrivateMessage方法处理发送私聊消息请求,与发送消息逻辑相同,只是接收方不同。
二、客户端实现
1. 客户端主类 MultiChatClientjavaimport 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; if (args.length > 0) { host = args[0]; } if (args.length > 1) { port = Integer.parseInt(args[1]); } new MultiChatClient(host, port).run(); }}
该类负责连接服务器并发送消息。
- 使用
NioEventLoopGroup创建线程池。- 使用Bootstrap配置客户端,包括线程池、通道类型、处理器等。- 在ChannelInitializer中添加StringDecoder和StringEncoder用于字符串编解码,以及MultiChatClientHandler用于处理业务逻辑。- 连接服务器后,从控制台读取用户输入并发送到服务器。
2. 消息处理器 MultiChatClientHandlerjavaimport io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;
public class MultiChatClientHandler extends SimpleChannelInboundHandler
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}
该类负责接收服务器端的消息并打印到控制台。
三、使用方法
- 编译并运行
MultiChatServer类启动服务器。2. 编译并运行多个MultiChatClient类启动客户端。3. 在客户端输入以下指令进行操作: - 登录:LOGIN|username|password- 查看在线用户列表:LIST- 发送消息:MSG|recipient|message- 发送私聊消息:PRIVATE|recipient|message
四、总结
本篇博客介绍了如何使用Netty构建一个基于Java的多人聊天室,包括服务器端和客户端的实现,以及用户登录、聊天室列表、消息发送和私聊功能。你可以根据自己的需要修改代码,添加更多功能。
原文地址: http://www.cveoy.top/t/topic/f1ZZ 著作权归作者所有。请勿转载和采集!