Getting started with Netty (NIO programming and Netty client server example)

1. NIO programming

1.1 What is NIO programming

One understanding is New I/O, because it is new compared to the previous I/O class library. More people like to call it non-blocking I/O (Non-block I/O). Since non-blocking I/O can better reflect the characteristics of NIO, subsequent NIO refers to non-blocking I/O.

1.2 Introduction to NIO class library

1. Buffer Buffer

	在面向流的I/O中,可以直接写入或者将数据直接读取到Stream对象中,在NIO库中,所有数据都是用缓冲区来处理的。在写入数据的时候,也是写入到缓冲区的。

The buffer is essentially an array, usually an array of bytes. The buffer provides structured access to data and maintains information such as read and write limits.

2. Channel channel

	通道与流不同之处在于通道是双向的,流知识在一个方向上面移动(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读、写或者二者同时进行。
	因为CHannel是双全工的,所以它比流更好的映射底层操作系统的API。特别是在UNIX网络编程模型中,底层的操作系统都是全双工的,同时支持读写操作。
	Channel可以分为两大类:用于网络读写的SelectableChannel和用于文件操作的FileChannel。ServerSocket和SocketChannel都是SelectableChannel的子类。

3. Multiplexer Selector

	多路复用器提供选择已经就绪的任务的能力。简单的说,Selector会不断的轮询地注册在上面的Channel,如果某个 Channel上面发生读或者写事件,这个Channel就会处于就绪状态,会被Selector轮询出来

4. Examples of NIO client and NIO server

public class NioClient {
    
    
    public static void client() throws IOException {
    
    
        // 获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        FileChannel inChannel = FileChannel.open(Paths.get("C:\\Users\\renyun\\Desktop\\图片\\logo2.png"), StandardOpenOption.READ);
        // 分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        // 读取本地文件,并发送到服务端
        while (inChannel.read(buf) != -1) {
    
    
            // 切换到读数据模式
            buf.flip();
            // 将缓冲区的数据写入管道
            sChannel.write(buf);
            // 清空缓冲区
            buf.clear();
        }

        //关闭通道
        inChannel.close();
        sChannel.close();
    }
    public static void main(String[] args) {
    
    
        new Thread(() -> {
    
    
            try {
    
    
                server();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }, "t1").start();
        try {
    
    
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        new Thread(() -> {
    
    
            try {
    
    
                client();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }, "t2").start();
    }
}
public class NioServer {
    
    
    /**
     * 服务端
     */
    public static void server() throws IOException {
    
    
        // 获取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        FileChannel fileChannel = FileChannel.open(Paths.get("C:\\Users\\renyun\\Desktop\\图片\\logo2.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        // 绑定端口号
        ssChannel.bind(new InetSocketAddress(9898));
        // 获取客户端连接的通道
        SocketChannel socketChannel = ssChannel.accept();
        // 分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        // 读取客户端的数据,并保存到本地
        while(socketChannel.read(buf) != -1) {
    
    
            // 切换成读模式
            buf.flip();
            // 写入
            fileChannel.write(buf);
            // 清空缓冲区
            buf.clear();
        }
        // 关闭通道
        ssChannel.close();
        socketChannel.close();
        fileChannel.close();
    }

}

5 reasons why you should not choose native NIO programming

1. The class library and API of NIO are complicated and troublesome to use.
2. Other additional skills are required, such as familiarity with multi-threaded programming. NIO involves Reactor mode, and you must be very familiar with multi-threaded and network programming.
3. I can’t afford to be happy, and the workload and difficulty are very large.
4. There are bugs in JDK NIO

2 Netty NIO Getting Started Guide

There are two versions of Netty4 and Netty, it is better to choose Netty4. The first step is to build the project. You can directly reference the Maven dependency package, or you can directly uninstall the jar package and copy it to the lib directory.
insert image description here

<!--netty依赖-->
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.20.Final</version>
    </dependency>
  </dependencies>

2.1 Netty entry server development


public class NettyServer {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try {
    
    
            bootstrap.group(bossGroup, worker)  //设置两个线程组
                    .channel(NioServerSocketChannel.class)  //作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG,128) //设置线程队列等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE,true) //设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() {
    
     //创建一个通道初始化对象
                        //给pipline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
    
    
                            channel.pipeline().addLast(new NettyServerHandler());

                        }

                    });  //
            System.out.println("服务器已经准备好了");
            //绑定一个端口并且同步处理,生成一个ChannelFuture对象

            ChannelFuture cf = bootstrap.bind(6668).sync();

            //对关闭通道进行监听 异步模型 channelFuture
            cf.channel().closeFuture().sync();
        }finally {
    
    
            bossGroup.shutdownGracefully();
            worker.shutdownGracefully();
        }



    }
}

Introduce the key class
NioEventLoopGroup, which is specially used for network event processing. In fact, it is the Reator thread group. There are two reasons for creating one for the server to accept connections from the client, and the other for SocketChannel network reading and writing.
ServerBootstrap is an auxiliary startup class used by Netty to start the server. The purpose is to reduce the development complexity of the server. By calling the group method, the two thread groups are passed as input parameters to the ServerSocketChannel class in the JDK NIO class library corresponding to NioServerSocketChannel in
ServerBootstrap

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    
    
    //读取数据实际(读取客户端消息) ChannelHandlerContext上下文对象
    // Object msg 客户端发送的消息 默认是object形式的
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        System.out.println("server ctx ="+ctx);
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("客户端发送消息"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("客户端地址"+ctx.channel().remoteAddress());
//        super.channelRead(ctx, msg);
        //将msg转成bytebuffer
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端",CharsetUtil.UTF_8));

    }
    //处理异常,一般是关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    
    
        ctx.close();
//        super.exceptionCaught(ctx, cause);
    }
}

NettyServerHandler inherits from ChannelInboundHandlerAdapter to read and write network events;
focus on channelRead and exceptionCaught methods.
ByteBuf buf = (ByteBuf) msg; convert msg to Netty's ByteBuf object. The ByteBuf method can obtain the number of bytes in the buffer.
From the above code, it can be seen that compared with the server side of the traditional JDK NIO native class library, the amount of code is greatly reduced, and the amount of development is greatly reduced.

2.2Netty client development

public class NettyClient {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        //客户端需要仅一个事件循环组
        EventLoopGroup eventExecutors = new NioEventLoopGroup();

        try{
    
    
            //创建客户端启动对象
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventExecutors)
                    .channel(NioSocketChannel.class) //设置客户端通道的处理
                    .handler(new ChannelInitializer<SocketChannel>() {
    
     //创建一个通道初始化对象
                        //给pipline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
    
    
                            channel.pipeline().addLast(new NettyClientHandler());

                        }

                    });
            System.out.println("客户端已经好了");
            ChannelFuture cf = bootstrap.connect("127.0.0.1", 6668).sync();
            cf.channel().closeFuture().sync();
        }finally {
    
    
            eventExecutors.shutdownGracefully();
        }
    }
}
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    
    

    //通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    
    
        System.out.println("client" +ctx);
        ctx.writeAndFlush(Unpooled.copiedBuffer("HELLO SERVER", CharsetUtil.UTF_8));
    }

    //当通道有读取事件时会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回复的消息"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器端的地址"+ctx.channel().remoteAddress());
    }

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


        cause.printStackTrace();
        ctx.close();
    }
}

The client's service is simpler than that of the server;

2.3 Running and Debugging

insert image description here
insert image description here
It should be pointed out that this routine does not consider the processing of half-package reading, and there is no problem for function demonstration or testing, but if stress testing is performed, it will not work normally, and knowledge points related to half-package need to be processed. Use some methods to solve TCP sticky packets and unpacking.

Guess you like

Origin blog.csdn.net/qq_21561833/article/details/122775815