4

核心组件

包括以下主要组件:

  • Channel
  • 回调
  • Future
  • 事件和ChannelHandler

这些组件代表了不同的构造:资源、逻辑、通知。

Channel

Channel是Java NIO的一个基本构造。
在网络通讯中,数据要从发送端应用程序->发送端硬件或则其他实体->服务端硬件或则其他实体->服务端应用,因此需要一个数据传输过程中的载体,这个载体就是Channel,它可以被打开或者被关闭,连接或则断开连接。

回调

一个回调就是一个方法,比如A方法在某些特定的流程中,调用B方法,这个B方法,就是A方法的回调。

Future

java并发编程学习中,我们也学到了这个对象,可以看做是对异步操作结果的一个占位符,在完成之前会一直阻塞,完成后提供对结果的访问。Netty也提供了自己的ChannelFuture的实现,用于在执行异步操作的时候使用。

事件和ChannelHandler

在Netty中,使用不同的事件来通知我们状态的改变或则状态的改变,以便我们基于这些事件来触发不同的动作。
这些事件包括出站入站等,我们可以针对这些事件定义不同的处理方式,比如记录日志、数据转换、流控制、业务逻辑处理等,这些处理方式,都是在ChannelHandler中实现的。

其他组件

EventLoopGroup

简单的说,他就是一个线程池,EventLoopGroup接口最终基础了ExecutorService接口。

引导

服务端引导是ServerBootstrap,客户端是Bootstrap。
ServerBootstrap用于配置Channel、EventLoopGroup、ChannelHandler以及监听并接受传入连接请求的端口。
Bootstrap与ServerBootstrap不同的是,使用服务端的地址和端口来连接服务端。

第一个Netty

服务端

服务端流程:

  1. 创建引导类ServerBootstrap
  2. 创建EventLoopGroup并设置到ServerBootstrap中
  3. 设置Channel
  4. 绑定本地端口
  5. 配置业务逻辑处理ChannelHandler
  6. 调用bind方法绑定服务器

EchoServer,通过ServerBootstrap引导类,配置不同的信息,这部分的代码也是通用性的,我们实际的业务处理逻辑在ChannelHandler上。

public class EchoServer {
    public static void main(String[] args) throws InterruptedException {
        final EchoServerHandler echoServerHandler = new EchoServerHandler();
        // 创建NioEventLoopGroup类型的EventLoopGroup
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 创建ServerBootstrap
            ServerBootstrap sbs = new ServerBootstrap();
            sbs.group(group)
                    // 设置Channel为NIO的服务端Channel
                    .channel(NioServerSocketChannel.class)
                    // 绑定本地端口
                    .localAddress(new InetSocketAddress(Const.PORT))
                    // 把echoServerHandler加入到ChannelPipeline中
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(echoServerHandler);
                        }
                    });
            // 异步绑定服务器,阻塞到服务器绑定完成
            ChannelFuture sync = sbs.bind().sync();
            // 获取channel的closeFuture,阻塞到关闭
            sync.channel().closeFuture().sync();
        } finally {
            // 优雅的关掉group并释放所有的资源
            group.shutdownGracefully().sync();
        }
    }
}

EchoServerHandler,实际的业务逻辑处理代码,这部分是根据我们的业务不同进行定制化开发的。

// 标记为ChannelHandler.Sharable时,标识能被多个channel安全的共享,是线程安全的
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 服务端接收客户端信息的时候调用
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        String request = byteBuf.toString(CharsetUtil.UTF_8);
        String resp = "Hello " + request;
        System.out.println("服务端收到信息:" + byteBuf.toString(CharsetUtil.UTF_8));
        // 把消息发送给客户端
        ctx.write(Unpooled.copiedBuffer(resp, CharsetUtil.UTF_8));
    }

    /**
     * 服务端处理客户端最后一条消息后调用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)// flush数据
                .addListener(ChannelFutureListener.CLOSE);// 关闭Channel
    }

    /**
     * 服务端处理消息过程中,对异常的处理
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 打印异常
        cause.printStackTrace();
        // 关闭Channel
        ctx.close();
    }
}

客户端

客户端流程:

  1. 创建引导类Bootstrap
  2. 创建EventLoopGroup并设置到Bootstrap中
  3. 设置Channel
  4. 绑定服务端地址和端口
  5. 配置业务逻辑处理ChannelHandler
  6. 连接服务端,等待处理完成后关闭连接

EchoClient

public class EchoClient {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 创建Bootstrap
            Bootstrap bs = new Bootstrap();
            //
            bs.group(group)
                    // 设置Channel为NIO的客户端Channel
                    .channel(NioSocketChannel.class)
                    // 设置服务器的地址端口信息
                    .remoteAddress(new InetSocketAddress(Const.IP, Const.PORT))
                    .handler(new EchoClientHandler());
            // 连接远程服务器,阻塞到连接完成
            ChannelFuture cf = bs.connect().sync();
            // 获取channel的closeFuture,阻塞到关闭
            cf.channel().closeFuture().sync();
        } finally {
            // 优雅的关掉group并释放所有的资源
            group.shutdownGracefully().sync();
        }
    }
}

EchoClientHandler

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    /**
     * 客户端收到服务器消息后调用
     *
     * @param channelHandlerContext
     * @param byteBuf
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        // 处理收到的消息
        System.out.println("客户端收到信息:" + byteBuf.toString(CharsetUtil.UTF_8));
    }

    /**
     * 客户端与服务器连接后调用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 当channel是活跃的时候,往服务端发送一条消息
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty", CharsetUtil.UTF_8));
    }

    /**
     * 客户端处理消息过程中,对异常的处理
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

大军
847 声望183 粉丝

学而不思则罔,思而不学则殆