基于 Netty 的多用户聊天室系统实现
基于 Netty 的多用户聊天室系统实现
本文将提供使用 Netty 框架构建一个多用户聊天室系统的详细代码实现,包含服务器端、客户端和消息处理逻辑。
一、服务器端
import 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类
import 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<String> {
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();
}
}
三、客户端
import 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类
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class MultiChatClientHandler extends SimpleChannelInboundHandler<String> {
@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();
}
}
运行示例
- 将以上代码分别保存为 MultiChatServer.java、MultiChatServerHandler.java、MultiChatClient.java 和 MultiChatClientHandler.java 文件。
- 编译代码:
javac *.java - 打包成jar文件:
jar cvf keshe.jar *.class
在终端运行服务器端:
- 打开终端,进入jar包所在的目录
- 输入命令:
java -jar keshe.jar - 服务器端就会启动,监听8080端口
在终端运行客户端:
- 打开终端,进入jar包所在的目录
- 输入命令:
java -jar keshe.jar - 客户端就会启动,连接到服务器端的8080端口
- 在客户端输入命令,例如:
LOGIN|username|password:登录聊天室,其中username和password是自己的账号和密码LIST:查看当前聊天室列表MSG|roomName|message:在指定聊天室发送消息,其中roomName是聊天室名称,message是要发送的消息PRIVATE|recipient|message:向指定用户发送私聊消息,其中recipient是接收者的用户名,message是要发送的消息
- 在客户端输入
exit命令,可以退出聊天室
注意:
- 由于使用了
ConcurrentHashMap来存储用户和对应连接的ChannelHandlerContext,确保了线程安全,可以方便地进行用户管理和消息广播。 - 每个客户端连接都拥有一个唯一的
ChannelHandlerContext,用于发送和接收消息。 - 代码中使用
StringDecoder和StringEncoder处理文本消息的编码和解码,方便进行消息传递和处理。 - 为了方便测试,示例代码中使用了简单的用户名和密码验证,实际应用中需要使用更安全的认证机制。
希望本文的代码示例能够帮助您更好地理解使用 Netty 框架构建多用户聊天室系统的原理和方法。
原文地址: http://www.cveoy.top/t/topic/f10n 著作权归作者所有。请勿转载和采集!