✅作者简介:热爱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>() {@Overrideprotected 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);// 当新的客户端连接加入时调用@Overridepublic 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}// 当客户端断开连接时调用@Overridepublic 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中移除断开的连接}// 当服务器接收到客户端发来的消息时调用@Overridepublic 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");}}}// 当出现异常时调用@Overridepublic 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>() {@Overrideprotected 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> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println(msg); // 输出从服务器收到的消息}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}
四、启动与测试
5.1 启动服务器和控制台客户端
- 启动
ChatServer
。 - 启动
ChatClient
,进行消息测试。
我们启动服务端ChatServer
接着再启动客户端ChatClient
客户端ChatClient
发送消息,并且服务端转发回来( [YOU]
那一行就是服务端转发回来的):
服务端接收到消息:
结语
本文通过实现简单的控制台客户端和HTML客户端,让大家更好地理解Netty服务器的工作原理。希望对大家有所帮助。