2025.5.16
Netty 小型聊天服务器项目
下面是一个基于Netty框架实现的简单聊天服务器项目,包含服务端和客户端代码。
1. 项目概述
这是一个简单的聊天系统,支持:
- 多客户端连接
- 广播消息给所有用户
- 简单的昵称设置
- 用户加入/离开通知
2. 服务端实现
2.1 服务端主类
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;
public class ChatServer {
private final int port;
public ChatServer(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 {
ChannelPipeline pipeline = ch.pipeline();
// 添加编解码器
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 添加业务处理器
pipeline.addLast("handler", new ChatServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
System.out.println("ChatServer started on port " + port);
// 绑定端口,开始接收连接
ChannelFuture f = b.bind(port).sync();
// 等待服务器socket关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("ChatServer stopped");
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new ChatServer(port).run();
}
}
2.2 服务端处理器
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
// 用于管理所有连接的Channel
private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
// 通知所有客户端有新用户加入
channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入聊天室\n");
channels.add(incoming);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
// 通知所有客户端有用户离开
channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开聊天室\n");
// 自动从ChannelGroup中移除,不需要手动操作
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Channel incoming = ctx.channel();
// 如果是设置昵称的命令
if (msg.startsWith("/nick ")) {
String nickname = msg.substring(6).trim();
incoming.attr(NICKNAME).set(nickname);
incoming.writeAndFlush("[SERVER] 你的昵称已设置为: " + nickname + "\n");
return;
}
// 广播消息给所有客户端
String nickname = incoming.attr(NICKNAME).get();
if (nickname == null) {
nickname = incoming.remoteAddress().toString();
}
for (Channel channel : channels) {
if (channel != incoming) {
channel.writeAndFlush("[" + nickname + "] " + msg + "\n");
} else {
channel.writeAndFlush("[你] " + msg + "\n");
}
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println("客户端连接: " + incoming.remoteAddress());
incoming.writeAndFlush("欢迎加入聊天室! 使用/nick <昵称>设置你的昵称\n");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println("客户端断开: " + incoming.remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
// 用于存储昵称的属性键
private static final AttributeKey<String> NICKNAME = AttributeKey.valueOf("nickname");
}
3. 客户端实现
3.1 客户端主类
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
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 ChatClient {
private final String host;
private final int port;
public ChatClient(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)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new ChatClientHandler());
}
});
Channel channel = b.connect(host, port).sync().channel();
System.out.println("Connected to chat server at " + host + ":" + port);
// 读取控制台输入
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String line = in.readLine();
if (line == null || "bye".equalsIgnoreCase(line)) {
break;
}
channel.writeAndFlush(line + "\r\n");
}
} 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 ChatClient(host, port).run();
}
}
3.2 客户端处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// 直接输出服务器返回的消息
System.out.print(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
4. 项目运行说明
4.1 编译和运行
- 首先编译所有Java文件:
javac -cp ".:netty-all-4.1.68.Final.jar" *.java
(假设你已经下载了Netty的JAR文件并放在当前目录)
- 启动服务器:
java -cp ".:netty-all-4.1.68.Final.jar" ChatServer
- 启动客户端(可以启动多个):
java -cp ".:netty-all-4.1.68.Final.jar" ChatClient
4.2 使用说明
- 连接后,可以使用
/nick 你的昵称
来设置你的昵称 - 直接输入消息回车即可发送给所有用户
- 输入"bye"退出客户端
5. 项目扩展建议
这个简单的聊天服务器可以进一步扩展:
- 添加私聊功能(如
/msg 用户名 消息内容
) - 添加用户列表查询(如
/list
) - 添加身份验证功能
- 支持更丰富的消息格式(如表情、图片等)
- 添加消息历史记录
- 支持房间/频道功能
6. 依赖说明
你需要Netty框架的JAR文件来运行这个项目。可以从Netty官网下载:https://netty.io/
这个项目使用的是Netty 4.1.x版本,你可以根据需要选择最新版本。
希望这个Netty小项目对你有所帮助!你可以基于这个框架继续扩展更复杂的功能。