Netty入门案例:简单Echo服务器(同步)

目录

1、添加 Netty 依赖

2、服务器端

3、客户端

4、运行步骤


1、添加 Netty 依赖


    io.netty
    netty-all
    4.1.68.Final 

2、服务器端

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;

public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        // 1、创建bossGroup线程组,处理连接请求,线程数默认:2*处理器线程数
        EventLoopGroup bossGroup = new NioEventLoopGroup(); 
        // 2、创建workerGroup线程组,处理业务(读写事件),线程数默认:2*处理器线程数
        EventLoopGroup workerGroup = new NioEventLoopGroup(); 
        
        try {
            // 3、创建服务端启动助手
            ServerBootstrap b = new ServerBootstrap();
            // 4、设置线程组
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // 5、设置服务端通道实现,使用NIO传输
             .option(ChannelOption.SO_BACKLOG, 128) // 6、设置连接队列大小
             .childOption(ChannelOption.SO_KEEPALIVE, true); // 7、保持长连接
             .childHandler(new ChannelInitializer() {// 8、创建一个通道初始化对象
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     // 9、向pipeline中添加自定义业务处理handler
                     ch.pipeline().addLast(new EchoServerHandler());
                 }
             })
            
            // 10、绑定端口并开始接收连接,同时将异步改为同步
            ChannelFuture f = b.bind(port).sync();
            System.out.println("EchoServer started and listen on " + f.channel().localAddress());
            
            // 11、等待服务器socket关闭
            f.channel().closeFuture().sync();
        } finally {
            // 12、关闭通道和连接池
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new EchoServer(port).start();
    }
}

解说:

1> ChannelFuture f = b.bind(port).sync();sync()的意义和作用:
  • 阻塞当前线程

    • .sync() 会阻塞调用它的线程(通常是主线程),直到异步操作(这里是 bind(port))完成

    • 没有 .sync() 时,方法会立即返回,而绑定操作可能在后台继续执行

  • 同步等待结果

    • 确保在 .sync() 之后的代码执行时,绑定操作已经完成(成功或失败)

    • 例如后面的 System.out.println("聊天服务器已启动") 一定是在绑定完成后执行

  • 异常传播

    • 如果绑定失败(如端口被占用),.sync() 会抛出异常

    • 没有 .sync() 时,异常会被静默处理,需要通过监听器捕获

2> future.channel().closeFuture().sync(); 
  • future.channel().closeFuture() 获取一个特殊的ChannelFuture,它仅在服务器的Channel关闭时才会完成

  • .sync() 会阻塞当前线程(通常是main线程),直到这个关闭事件发生

  • 服务器会一直运行,直到有人主动触发关闭操作,例如:

    • 调用 channel.close() 方法

    • 程序收到终止信号(如Ctrl+C)

    • 通过管理接口发送关闭命令

    • 发生不可恢复的错误

3> 

finally {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}
  • 当 closeFuture().sync() 返回(即服务器Channel已关闭)后

  • 程序会进入 finally 块

  • 优雅关闭两个线程组:

    • bossGroup:接收新连接的线程组

    • workerGroup:处理I/O操作的线程组

  • shutdownGracefully() 会:

    1. 停止接收新连接

    2. 等待现有任务完成

    3. 释放所有关联资源

服务器端处理器:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 通道读取事件
     *
     * @param ctx 通道上下文对象
     * @param msg 消息
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("Server received: " + in.toString(io.netty.util.CharsetUtil.UTF_8));
        ctx.write(in); // 将接收到的消息回写给发送者,而不冲刷出站消息
    }
    /**
     * 读取完毕事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        //ctx.writeAndFlush(Unpooled.copiedBuffer("你好,我是Netty服务端.", CharsetUtil.UTF_8));
        ctx.flush(); // 将未决消息冲刷到远程节点,并关闭该Channel
    }
    /**
     * 异常发生事件
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close(); // 关闭该Channel
    }
}

3、客户端

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.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

public class EchoClient {
    private final String host;
    private final int port;

    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws Exception {
        // 1、创建线程组
        EventLoopGroup group = new NioEventLoopGroup();
        
        try {
            // 2、创建客户端启动助手
            Bootstrap b = new Bootstrap();
            // 3、设置线程组
            b.group(group)
             .channel(NioSocketChannel.class) //4、设置服务端通道实现为NIO
             .handler(new ChannelInitializer() { //5、创建一个通道初始化对象
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     //6、向pipeline中添加自定义业务处理handler
                     ch.pipeline().addLast(new EchoClientHandler());
                 }
             });
            
            // 7、连接到服务器,将异步改为同步
            ChannelFuture f = b.connect(host, port).sync();
            System.out.println("Connected to server");
            
            // 8、发送消息
            String message = "Hello, Netty!";
            ByteBuf buf = Unpooled.copiedBuffer(message.getBytes());
            f.channel().writeAndFlush(buf);
            
            // 9、等待连接关闭
            f.channel().closeFuture().sync();
        } finally {
            // 10、关闭连接池
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new EchoClient("localhost", 8080).start();
    }
}

客户端处理器:

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class EchoClientHandler extends ChannelInboundHandlerAdapter {
    /**
     * 通道就绪事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("你好呀,我是Netty客户端", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("Client received: " + in.toString(io.netty.util.CharsetUtil.UTF_8));
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

4、运行步骤

  1. 首先启动 EchoServer,它将监听 8080 端口

  2. 然后启动 EchoClient,它将连接到服务器并发送一条消息

  3. 服务器会将接收到的消息回传给客户端

  4. 将在客户端控制台看到服务器返回的消息

你可能感兴趣的:(12_计算机网络,服务器,java)