-
服务器启动代码(SimpleServer.java):
-
EventLoopGroup
的作用是管理事件循环组,它们负责处理不同类型的事件,如接受传入连接、读取/写入数据等。在这里,我们创建了两个EventLoopGroup
:bossGroup
和workerGroup
。bossGroup
用于接受传入的连接请求,而workerGroup
则用于实际处理连接的流量。 -
ServerBootstrap
是 Netty 用于启动服务器的帮助类。通过group()
方法将两个EventLoopGroup
绑定到服务器上。 -
channel()
方法指定了服务器使用的通道类型。在这里,我们选择了NioServerSocketChannel.class
,这意味着我们将使用基于 NIO 的 Socket 通道。 -
childHandler()
方法设置了一个ChannelInitializer
,当一个新的连接被接受时,这个初始化器将会被调用。在这里,我们为新连接的通道添加了一个处理器,即SimpleServerHandler
。 -
最后,我们通过
bind()
方法绑定了服务器端口,并调用sync()
方法来阻塞,直到服务器关闭。这确保了服务器在启动后保持运行。
TCP 服务器示例
import io.netty.bootstrap.ServerBootstrap; 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.NioServerSocketChannel; public class SimpleServer { public static void main(String[] args) throws InterruptedException { // 创建两个 EventLoopGroup,用于处理连接和处理数据 EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 处理连接的线程组 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理数据的线程组 try { // 创建 ServerBootstrap 对象来启动服务器 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 使用 NIO 通道 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加处理器到管道中 ch.pipeline().addLast(new SimpleServerHandler()); } }); // 绑定端口并启动服务器 ChannelFuture channelFuture = serverBootstrap.bind(8888).sync(); channelFuture.channel().closeFuture().sync(); } finally { // 优雅关闭线程组 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
-
-
服务器处理器(SimpleServerHandler.java):
-
SimpleServerHandler
是一个自定义的ChannelInboundHandlerAdapter
的子类,用于处理从客户端接收到的消息。 -
当有数据从客户端传入时,
channelRead()
方法将会被调用。在这里,我们将传入的消息转换为ByteBuf
对象,并在控制台上打印其内容。 -
如果发生异常,
exceptionCaught()
方法将被调用,我们在这里简单地打印异常信息并关闭连接。这是一种良好的做法,以确保服务器在出现问题时能够优雅地关闭连接并释放资源。服务器处理器示例:
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class SimpleServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 读取客户端发送的消息 ByteBuf buf = (ByteBuf) msg; try { while (buf.isReadable()) { // 输出消息内容到控制台 System.out.print((char) buf.readByte()); System.out.flush(); } } finally { // 释放资源 buf.release(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 异常处理 cause.printStackTrace(); ctx.close(); } }
-
-
客户端启动代码(SimpleClient.java):
-
与服务器端启动代码类似,我们也需要一个
EventLoopGroup
来处理客户端连接。在这里,我们只需要一个EventLoopGroup
。 -
Bootstrap
是用于启动客户端的帮助类。我们通过group()
方法将EventLoopGroup
绑定到客户端上。 -
channel()
方法指定了客户端使用的通道类型。在这里,我们同样选择了基于 NIO 的 Socket 通道。 -
handler()
方法设置了一个ChannelInitializer
,用于在连接建立时配置新的通道。在这里,我们添加了一个SimpleClientHandler
来处理客户端的连接。 -
最后,我们通过
connect()
方法连接到服务器,并调用sync()
方法来阻塞,直到连接关闭。这确保了客户端在连接建立后保持运行。
TCP 客户端示例
import io.netty.bootstrap.Bootstrap; 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; public class SimpleClient { public static void main(String[] args) throws InterruptedException { // 创建一个 EventLoopGroup 用于处理连接 EventLoopGroup group = new NioEventLoopGroup(); try { // 创建 Bootstrap 对象来启动客户端 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) // 使用 NIO 通道 .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加处理器到管道中 ch.pipeline().addLast(new SimpleClientHandler()); } }); // 连接到服务器 ChannelFuture channelFuture = bootstrap.connect("localhost", 8888).sync(); // 阻塞直到连接关闭 channelFuture.channel().closeFuture().sync(); } finally { // 优雅关闭线程组 group.shutdownGracefully(); } } }
-
-
客户端处理器(SimpleClientHandler.java):
-
SimpleClientHandler
同样是一个自定义的ChannelInboundHandlerAdapter
的子类,用于处理从服务器接收到的响应消息。 -
当连接建立并且数据可读时,
channelRead()
方法将会被调用。在这里,我们将从服务器接收到的消息转换为ByteBuf
对象,并在控制台上打印其内容。
-
-
如果发生异常,
exceptionCaught()
方法将被调用,我们在这里简单地打印异常信息并关闭连接。这也是一种良好的做法,以确保客户端在出现问题时能够优雅地关闭连接并释放资源。
客户端处理器示例:
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class SimpleClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // 当连接建立时发送消息到服务器 ByteBuf buf = ctx.alloc().buffer(); buf.writeBytes("Hello from client".getBytes()); ctx.writeAndFlush(buf); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 读取服务器发送的消息并输出到控制台 ByteBuf buf = (ByteBuf) msg; try { while (buf.isReadable()) { System.out.print((char) buf.readByte()); System.out.flush(); } } finally { buf.release(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 异常处理 cause.printStackTrace(); ctx.close(); } }