Netty入门应用

Netty入门应用

一、开发环境搭建

前提:电脑有Java环境,并且jdk版本1.7+。熟悉Java基础,maven。

1.1创建一个基础maven项目

1.file -> new -> project

在这里插入图片描述

2.创建maven项目,添加相关信息,然后点击create

在这里插入图片描述

3.等待idea下载依赖,如果失败,需要配置maven仓库

在这里插入图片描述

1.2补充pom文件

1.2.1 前往选择Netty版本

网址为:

https://mvnrepository.com/artifact/io.netty/netty-all

实际上,Netty4.1.97.Final的使用者更多,但是笔者跟着《Netty权威指南 第二版》学习的,所以选择的是5.0.0.Alpha1

在这里插入图片描述

点击进入,然后复制下来:

在这里插入图片描述

maven:

<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>5.0.0.Alpha1</version>
</dependency>

复制到pom.xml文件中,点击如图m重构maven,下载依赖:

在这里插入图片描述

二、Netty服务端开发

以一个基础的时间服务器为例进行开发

2.1编写服务端TimeServer类

代码如下:

public class TimeServer {
    public void bind(int port){
        //配置服务端NIO线程组
        //这里创建两个NioEventLoopGroup实例,一个用户服务端接受客户端连接,一个用于SocketChannel的读写
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try{
            //ServerBootstrap是用于启动NIO服务端的辅助启动类
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup) //将上面两个group当成参数传入ServerBootstarp
                    .channel(NioServerSocketChannel.class)//设置通道为NioServerSocketChannel
                    .option(ChannelOption.SO_BACKLOG,1024)//配置NioServerSocketChannel的tcp参数
                    .childHandler(new ChildChannelHandler());//绑定实际的事件处理类

            //绑定端口,等待连接
            ChannelFuture channelFuture = b.bind(port).syncUninterruptibly();

            //等待服务器监听关闭
            //使用这个方法进行阻塞,等待服务端链路关闭之后main函数才退出
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //优雅退出,该函数会关闭跟shutdownGracefully相关联的资源
            bossGroup.shutdownGracefully();//关闭线程
            workerGroup.shutdownGracefully();
        }


    }

    //IO事件处理类
    private static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            socketChannel.pipeline().addLast(new TimeServerHandler());
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if (args!=null && args.length > 0) {
            try{
                port = Integer.parseInt(args[0]);
            }catch (NumberFormatException e){
                System.out.println(e.getMessage());
            }
        }

        new TimeServer().bind(port);
    }
}

2.2编写服务端TimeServerHandler类

public class TimeServerHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //这里进行转换,因为ByteBuf提供了更强大灵活的功能
        ByteBuf buf = (ByteBuf) msg;
        //readableBytes()方法可以获取缓冲区可读的字节数,以此创建一个合适大小的byte数组
        byte[] bytes = new byte[buf.readableBytes()];
        //readBytes方法将buf中缓存的字节复制到新建的bytes数组中
        buf.readBytes(bytes);
        //将数组转为字符
        String s = new String(bytes, StandardCharsets.UTF_8);
        System.out.println("The time is " + s);
        //QUERY TIME ORDER这串字符是客户端发送过来的,这里判断请求消息来源,如果是客户端的消息,则将正确时间赋值给currentTime
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(s) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
        //转为ByteBuf对象
        ByteBuf byteBuf = Unpooled.copiedBuffer(currentTime.getBytes());
        //异步发送应答消息给客户端
        ctx.write(byteBuf);

    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

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

相关的代码解释已经注释在代码里面,如果理解不了也没关系。本次仅仅是一个入门应用,仅做一次基本的解释。

三、客户端开发

3.1编写客户端TimeClient

public class TimeClient {

    public void connect(int port, String host) {
        //客户端处理IO事件的线程组
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        //客户端辅助启动类
        Bootstrap bootstrap = new Bootstrap();
        try {
            Bootstrap channel = bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class)//设置channel为NioSocketChannel
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() { //匿名内部类的方式,实现一个init方法
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeClientHandler());
                        }
                    });

            //发起异步连接操作,等待连接
            ChannelFuture sync = bootstrap.connect(host, port).sync();

            //等待客户端链路关闭,异步阻塞
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            //优雅退出,关闭NIO线程
            nioEventLoopGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        int port = 8080;
        if(args!=null && args.length>0){
            try{
                port = Integer.parseInt(args[0]);
            }catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        new TimeClient().connect(port, "127.0.0.1");
    }
}

3.2编写客户端TimeClientHandler

public class TimeClientHandler extends ChannelHandlerAdapter {
    private static final Logger logger = Logger
            .getLogger(TimeClientHandler.class.getName());

    private final ByteBuf firstMessage;

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

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

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        String s = new String(bytes, StandardCharsets.UTF_8);
        System.out.println("Now is : "+s);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //释放资源
        logger.warning("Unexpected exception from downstream : "+cause.getMessage());
        ctx.close();
    }
}

这里重点关注三个方法:channelActive、channelRead和exceptionCaught。当客户端和服务端TCP链路建立成功之后,Netty的Nio线程会调用channelActive方法,发送查询时间的指令给服务端,调用ChannelHandlerContext的writeAndFlush方法将请求发送给服务端。

当服务端返回应答消息时,channelRead方法被调用。

四、运行

在idea中,右键TimeServer,点击启动按钮如图:

在这里插入图片描述

同样的方法启动客户端TimeClient:

在这里插入图片描述

我们来查看控制台:

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值