Netty开发入门

Posted 落叶飞翔的蜗牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty开发入门相关的知识,希望对你有一定的参考价值。







Netty编程之HelloWorld

通过Netty的HelloWorld与NIO的HelloWord进行对比

分析一下两这个开发的复杂度,来证明Netty的意义








既然有了NIO,为什么还需要Netty



  1. 创建ServerSocketChannel,配置为非阻塞

  2. 绑定监听,配置TCP参数

  3. 创建一个独立的IO线程,用户轮询多路复用器Selector

  4. 创建Selector,将ServerSocketChannel注册到Selector上,监听ACCEPT事件

  5. 启动IO线程,在循环体中执行Selector.select()方法

  6. 轮询到就绪的Channel,对其进行判断,如果是ACCEPT状态,说明是客户端接入,调用ServerSocketChannel.accept()接收新的客户端

  7. 设置新接入客户端的SocketChannel为非阻塞,设置TCP参数

  8. 将SocketChannel注册到Selector,监听READ操作

  9. 如果轮询事件是READ, 说明SocketChannel有新的数据包就绪,构造ByteBuffer对象,读取数据包

  10. 如果轮询到WRITE事件,说明还有数据没有发送完,需要继续发送

可以发现,用NIO编程非常复杂,使用Netty编程可以简化开发。



1
Netty服务器端开发



public class TimeServer {

    public void bind(int port) {
        //配置服务器端NIO线程组
        //NioEventLoopGroup是个线程组,包含了一组NIO线程,处理网络事件,实际上就是Reactor线程组
        try (EventLoopGroup bossLoopGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup()){
            //netty用于启动NIO服务端的启动类,目的是降低NIO开发的复杂度
            ServerBootstrap bootstrap = new ServerBootstrap();
            //功能类似于NIO中的ServerSocketChannel
            bootstrap.group(bossLoopGroup, workerGroup).channel(NioserverSocketChannel.class)
                    //配置NioServerSocketChannel的参数
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //绑定事件的处理类ChildChannelHandler
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeServerHandler());
                        }
                    });
            //绑定端口,同步等待绑定操作完成
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            //等待服务器监听端口关闭
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        int port = 8888;
        new TimeServer().bind(port);
    }
}


服务器端处理类ChildChannelHandler具体处理逻辑如下:

public class TimeServerHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        //类似NIO中的ByteBuffer
        ByteBuf buf = (ByteBuf) msg;
        //获取缓冲区可读字节数
        byte[] req = new byte[buf.readableBytes()];
        //缓冲区中的字节复制到字节数组
        buf.readBytes(req);
        String body = new String(req);
        System.out.println("收到输入:" + body);
        ByteBuf response = Unpooled.copiedBuffer(("当前时间:" + new Date()).getBytes());
        //并不是直接把消息发送到SocketChannel中,只是把消息发送到缓冲数组,通过flush方法将消息发到SocketChannel
        ctx.write(response);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        //将消息发送队列中的消息写入SocketChannel中,发送到对方
        //防止频繁的唤醒Selector进行消息发送
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        //发生异常关闭ChannelHandlerContext等资源
        ctx.close();
    }
}


Netty开发入门

2
Netty客户端开发


public class TimeClient {

    public void connect(int port, String host) {
        try (EventLoopGroup eventLoopGroup = new NioEventLoopGroup()){
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            //异步链接操作
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            //等待客户端
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        int port = 8888;
        new TimeClient().connect(port, "127.0.0.1");
    }
}


客户端处理类TimeClientHandler具体处理逻辑如下:

public class TimeClientHandler extends ChannelHandlerAdapter {

    private final ByteBuf firstMessage;

    public TimeClientHandler() {
        byte[] req = "hello world".getBytes();
        firstMessage = Unpooled.buffer(req.length);
        firstMessage.writeBytes(req);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(firstMessage);
    }

    /**
     * 读取并打印消息
     * @param ctx
     * @param msg
     * @throws Exception
     */

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] resp = new byte[buf.readableBytes()];
        buf.readBytes(resp);
        String body = new String(resp);
        System.out.println(body);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}



总结




可以发现,相比于直接用NIO开发,netty代码更加简洁,开发难度更低,扩展性更好。






以上是关于Netty开发入门的主要内容,如果未能解决你的问题,请参考以下文章

Netty开发入门

推荐net开发cad入门阅读代码片段

九.Netty入门到超神系列-Netty开发Http服务器

Netty实战入门详解——让你彻底记住什么是Netty(看不懂你来找我)

Netty|01 入门学习

Netty入门