✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。
🍎个人主页:Hhzzy99
🍊个人信条:坚持就是胜利!
💞当前专栏:Netty
🥭本文内容:Netty实现一个简单的聊天服务器。
文章目录
- 使用Netty实现一个简单的聊天服务器
- 一、项目初始化与依赖配置
- 1.1 创建Maven项目并添加依赖
- 二、服务器端代码实现
- 2.1 `ChatServer` - 服务端启动类
- 2.2 `ChatServerHandler` - 消息处理器
- 三、控制台客户端实现
- 3.1 `ChatClient` - 客户端启动类
- 3.2 `ChatClientHandler` - 客户端消息处理器
- 四、启动与测试
- 5.1 启动服务器和控制台客户端
- 结语
使用Netty实现一个简单的聊天服务器
在本篇教程中,我们将通过Java的Netty框架实现一个简单的聊天服务器。
Netty是一款基于NIO(非阻塞IO)开发的网络框架,适用于高性能、高并发的应用场景。本文会详细展示如何通过Netty实现聊天服务器,实现控制台版客户端。
一、项目初始化与依赖配置
1.1 创建Maven项目并添加依赖
我们使用Maven管理依赖,首先在pom.xml
文件中添加Netty依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>netty-chat</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Netty 依赖 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
</dependency>
</dependencies>
</project>
这样配置完成后,Maven会自动下载Netty的相关包。
二、服务器端代码实现
服务器端负责接受客户端连接、广播消息,并在客户端断开时进行处理。接下来我们编写服务器端启动类和处理器。
2.1 ChatServer
- 服务端启动类
ChatServer
类用于配置Netty服务器的基础设置,包括监听的端口、通道类型(NIO模式)和处理器链。服务器会监听指定端口,并通过处理器将消息分发给各客户端。
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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;
public class ChatServer {
private final int port;
public ChatServer(int port) {
this.port = port; // 设置服务器监听的端口
}
public void start() throws InterruptedException {
// bossGroup:接受客户端连接的线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// workerGroup:处理客户端连接的数据传输
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// ServerBootstrap用于启动和配置Netty服务器
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 设置boss和worker线程组
.channel(NioServerSocketChannel.class) // 指定NIO传输的通道类型
.option(ChannelOption.SO_BACKLOG, 1024) // 设置队列容量
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
// 为每个客户端连接配置消息处理器
ch.pipeline().addLast(new ChatServerHandler());
}
});
// 绑定端口并启动服务器
ChannelFuture f = b.bind(port).sync();
System.out.println("Chat server started on port " + port);
// 等待服务器关闭
f.channel().closeFuture().sync();
} finally {
// 关闭资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
int port = 8080; // 定义服务器监听的端口号
new ChatServer(port).start(); // 启动服务器
}
}
2.2 ChatServerHandler
- 消息处理器
ChatServerHandler
类负责管理客户端连接和消息的广播。我们会在客户端加入时广播“用户加入”消息,并在消息接收时进行广播。
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
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 ChannelInboundHandlerAdapter {
// 使用ChannelGroup来保存所有的连接,便于消息广播
private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
// 当新的客户端连接加入时调用
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
// 广播消息给所有已连接的客户端,通知新的连接加入
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " joined\n");
}
channels.add(incoming); // 将新连接加入ChannelGroup
}
// 当客户端断开连接时调用
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel outgoing = ctx.channel();
// 广播消息给所有已连接的客户端,通知连接已断开
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + outgoing.remoteAddress() + " left\n");
}
channels.remove(outgoing); // 从ChannelGroup中移除断开的连接
}
// 当服务器接收到客户端发来的消息时调用
@Override
public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception {
Channel incoming = ctx.channel();
// 使用StringDecoder与StringEncoder编码解码消息,所以直接字符串
String msg = (String) message;
System.out.println("[client] : " + msg);
// 向其他客户端广播消息,当前发送者不接收自己的消息
for (Channel channel : channels) {
if (channel != incoming) {
channel.writeAndFlush("[" + incoming.remoteAddress() + "] " + msg + "\n");
} else {
channel.writeAndFlush("[you] " + msg + "\n");
}
}
}
// 当出现异常时调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close(); // 关闭连接
}
}
三、控制台客户端实现
在实现浏览器客户端之前,我们先用Java创建一个简单的控制台客户端,以便在控制台中测试服务器的基本功能。通过这种方式,我们可以在不使用HTML页面的情况下,验证服务器的消息广播和客户端连接是否正常工作。
3.1 ChatClient
- 客户端启动类
ChatClient
类用于建立与服务器的连接,并在连接成功后允许用户发送消息。
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.util.Scanner;
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 start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new ChatClientHandler());
}
});
Channel channel = b.connect(host, port).sync().channel();
Scanner scanner = new Scanner(System.in);
System.out.println("Enter your messages (type 'exit' to quit):");
while (true) {
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
channel.writeAndFlush(message + "\n");
}
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new ChatClient("localhost", 8080).start();
}
}
3.2 ChatClientHandler
- 客户端消息处理器
ChatClientHandler
类用于接收服务器的消息并在控制台显示。
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg); // 输出从服务器收到的消息
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
四、启动与测试
5.1 启动服务器和控制台客户端
- 启动
ChatServer
。 - 启动
ChatClient
,进行消息测试。
我们启动服务端ChatServer
接着再启动客户端ChatClient
客户端ChatClient
发送消息,并且服务端转发回来( [YOU]
那一行就是服务端转发回来的):
服务端接收到消息:
结语
本文通过实现简单的控制台客户端和HTML客户端,让大家更好地理解Netty服务器的工作原理。希望对大家有所帮助。