1. ChannelHandler和业务逻辑

@Sharable // 一个ChannelHandler可以被多个Channel共享
public class EchoServiceHandler extends ChannelInboundHandlerAdapter { 
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // 对每个传入的消息都要调用
        ByteBuf in = (ByteBuf) msg;
        ctx.write(in); // 将接收到的消息写给发送者
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) { // 通知ChannelInboundHandler对channelRead()的调用是当前批量中的最后一条
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); // 冲刷到远程节点并关闭监听
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 读取操作期间有异常抛出时调用
        cause.printStackTrace(); // 打印异常栈跟踪
        ctx.close(); // 关闭Channel
    }
}

2. 引导服务器

public class EchoService {
    private final int port;
    
    public EchoService (int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        int port = Integer.parseInt(args[0]);
        new EchoService(port).start();
    }

    public void start() throws Exception {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        EventLoopGroup group = new NioEventLoopGroup(); // 进行事件的处理,如接受新连接及读写数据
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                .channel(NioServerSocketChannel.class) // 指定所使用的NIO传输Channel
                .localAddress(new InetSocketAddress(port)) // 使用指定的端口设置套接字地址,服务器将绑定到这个地址监听新的连接请求
                .childHandler(new ChannelInitializer<SocketChannel>(){ // 一个新的连接会创建一个新的子Channel,ChannelInitializer将会把EchoServiceHandler实例添加到该Channel的ChannelPipeline中
                @Override 
                public void initChannel(SocketChannel ch) throws 
                    Exception { 
                        ch.pipeline().addLast(serverHandler); 
                    } 
                }); 
            ChannelFuture f = b.bind().sync(); // 异步绑定服务器,调用sync()方法阻塞等待直到绑定完成 
            f.channel().closeFuture().sync(); 
        } finally { 
                group.shutdownGracefully().sync(); // 释放所有资源,包括所有被创建的线程 
        } 
    } 
}
 

3. 通过ChannelHandler实现客户端逻辑

@Sharable
public class EcheClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) { // 与服务器建立连接后调用
        ctx.writeAndFlush(Unpooled.copiedBuffer("...", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) { // 从服务器接受消息时调用
        System.out.println("client received" + in.toString(CharsetUtil.UTF_8));    
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 发生异常时记录错误并关闭Channel
        cause.printStackTrace();
        ctx.close();        
    }
}

4. 引导客户端

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

    public static void main(String[] args) throws Exception {
        String host = args[0];
        int port = Integer.parseInt(args[1]);
        new EchoClient(host, port).start();
    }

    public void start() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup(); 
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioServerSocketChannel.class) 
                .remoteAddress(new InetSocketAddress(host, port)) 
                .handler(new ChannelInitializer<SocketChannel>(){ 
                @Override 
                public void initChannel(SocketChannel ch) throws 
                    Exception { 
                        ch.pipeline().addLast(new EchoClientHandler()); 
                    } 
                }); 
            ChannelFuture f = b.connect().sync(); 
            f.channel().closeFuture().sync(); 
        } finally { 
                group.shutdownGracefully().sync(); 
        } 
    } 
}