1. netty入门

  1. 准备工作
    构建gradle工程:build.gradle内容如下
    netty版本:compile group: ‘io.netty’, name: ‘netty-all’, version: ‘5.0.0.Alpha1’
group 'com.ghq.netty'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    // https://mvnrepository.com/artifact/io.netty/netty-all
    //compile group: 'io.netty', name: 'netty-all', version: '4.1.24.Final'
    // https://mvnrepository.com/artifact/io.netty/netty-all
    compile group: 'io.netty', name: 'netty-all', version: '5.0.0.Alpha1'


}
group 'com.ghq.netty'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

2.TimeServer服务端开发

TimeServer.java代码如下:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class TimeServer {

    public void bind(int port) throws Exception{
        /**
         * 创建两个异步线程组,专门用来处理网络事件,实际上就是Reactor线程组
         * 为什么创建两个?
         * 1. 一个用于服务器接受客户端的链接
         * 2. 用于进行SocketChannel的网络读写
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            /**
             * ServerBootstrap:是netty用于启动nio服务的辅助启动类,
             * 目的是降低服务器端的开发复杂度
             */
            ServerBootstrap bootstrap = new ServerBootstrap();
            /**
             * bootstrap.group(bossGroup, workerGroup)
             * 将两个线程组添加到bootstrap中。
             */
            bootstrap.group(bossGroup, workerGroup)
                //设置创建的Channel为 NioServerSocketChannel
                .channel(NioServerSocketChannel.class)
                // 配置TCP参数:backlog 为1024
                .option(ChannelOption.SO_BACKLOG, 1024)
                //绑定IO事件处理类 ChildChannelHandler
                .childHandler(new ChildChannelHandler());
            //绑定端口,同步等待成功
            ChannelFuture future = bootstrap.bind(port).sync();

            //等待服务器监听端口关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{

        @Override
        protected void initChannel(SocketChannel arg0) throws Exception {

            arg0.pipeline().addLast(new TimeServerHandler());
        }

    }

    public static void main(String[] args) throws Exception {
        int port = 9999;
        if(args != null && args.length > 0) {

            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

        new TimeServer().bind(port);
    }

}

TimeServerHandler.java代码如下:


import java.util.Date;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeServerHandler extends ChannelHandlerAdapter{

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String name = msg.getClass().getName();
        System.out.println(name);
        /**
         * ByteBuf 类似java.nio.ByteBuffer
         * 
         */
        ByteBuf buf = (ByteBuf) msg;

        /**
         * buf.readableBytes():获取缓冲区中可读的字节数
         * 根据该个数创建字节数组
         */
        byte[] req = new byte[buf.readableBytes()];

        /**
         * 将缓冲区的字节数组读取到新创建的数组req中
         */
        buf.readBytes(req);

        /**
         * 将字节数组req内容转换成String,获取到请求消息
         */
        String body = new String(req,"UTF-8");

        System.out.println("the time server receive order : "+ body);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?
                new Date(System.currentTimeMillis()).toString():"BAD ORDER";
        /**
         * 创建应答消息
         */
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());

        /**
         * 将消息异步响应给客户端
         */
        ctx.write(resp);
    };


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        /**
         * 将消息发送到队列中的消息写入到SocketChannel中发送给对方。
         * 从性能角度考虑,为了防止频繁的唤醒Selector进行消息发送,
         * Netty的write方法并不直接将消息写入到SocketChannel中,
         * 调用write方法只是把待发送的消息方法发送的缓冲数组中,
         * 再通过flush方法,将发生缓冲区的消息全部写到SocketChannel中。
         */
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        /**
         * 发生异常时,关闭ChannelHandlerContext
         */
        ctx.close();
    }
}

3.客户端开发
TimeClient.java代码:


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class TimeClient {

    public void connect(int port,String host) throws Exception{

        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new TimeClientHandler());

                    }
                });
            //发起异步链接操作
            ChannelFuture future = bootstrap.connect(host, port).sync();

            //等待客户端链路关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }


    }


    public static void main(String[] args) throws Exception {
        int port = 9999;
        if(args != null && args.length > 0) {

            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

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

}

TimeClientHandler.java代码如下:


import java.util.logging.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

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[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req,"UTF-8");
        System.out.println("Now is : "+ body);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        logger.warning("Unexpected exception from downstream : "+cause.getMessage());
        /**
         * 发生异常时,关闭ChannelHandlerContext
         */
        ctx.close();
    }
}

测试结果(先启动TimeServer,再启动TimeClient)如下:

服务端结果:

io.netty.buffer.SimpleLeakAwareByteBuf
the time server receive order : QUERY TIME ORDER

客户端结果:

Now is : Sat Jun 30 17:52:02 CST 2018

猜你喜欢

转载自blog.csdn.net/guo20082200/article/details/80868548