前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Netty开发入门

Netty开发入门

作者头像
黑洞代码
发布2021-01-14 15:37:42
3920
发布2021-01-14 15:37:42
举报
文章被收录于专栏:落叶飞翔的蜗牛

Netty编程之HelloWorld

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

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

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

回顾NIO的步骤【点击这里】:

  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服务器端开发

代码语言:javascript
复制
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具体处理逻辑如下:

代码语言:javascript
复制
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();
    }
}

2

Netty客户端开发

代码语言:javascript
复制
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具体处理逻辑如下:

代码语言:javascript
复制
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是如何处理TCP粘包和拆包等问题的,敬请关注

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 落叶飞翔的蜗牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档