netty 入门(整合springboot)

  1. 什么是netty?
    Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API的客户端/服务器框架。Netty 提供高性能和可扩展性,让你可以自由地专注于你真正感兴趣的东西,你的独特的应用!
    2.推荐阅读书籍:netty实战
    阅读地址:https://waylau.gitbooks.io/essential-netty-in-action/
    3.springboot 整合netty
    1.引入pom.xml
<!-- netty-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.6.Final</version>
        </dependency>

2.EchoServerHandler

package com.hzm.myproject.netty.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * @Author:huangzhimin
 * @Date:2020/6/8
 * @描述:echo 服务端 处理消息的业务逻辑
 *
 **/
@ChannelHandler.Sharable   //@Sharable 标识这类的实例之间可以在 channel 里面共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 每个信息入站都会被调用
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        //将日志消息是输出到控制台
        System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
        //将所接收的消息返回给发送者。注意,这还没有冲刷数据
        ctx.write(in);
    }

    /**
     * 通知处理器最后的 channelRead() 是当前批处理中的最后一条消息时调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
       // 冲刷所有待审消息到远程节点。关闭通道后,操作完成
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * 读操作时捕获到异常时调用
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace(); //5 打印异常堆栈跟踪
        ctx.close(); //6 关闭通道
    }
}

3.EchoServer

package com.hzm.myproject.netty.echo;

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

import java.net.InetSocketAddress;

/**
 * @Author:huangzhimin
 * @Date:2020/6/8
 * @描述:引导服务
 * 1.监听和接收进来的连接请求
 * 2.配置 Channel 来通知一个关于入站消息的 EchoServerHandler 实例
 **/
public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
//        if (args.length != 1) {
//            System.err.println("Usage: " + EchoServer.class.getSimpleName() + " <port>");
//            return;
//        }
        //设置端口值(抛出一个 NumberFormatException 如果该端口参数的格式不正确)
        int port = Integer.parseInt("9999");
        //呼叫服务器的 start() 方法
        new EchoServer(port).start();
    }

    public void start() throws Exception {
        //创建 EventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup(); //3
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            //创建 ServerBootstrap
            bootstrap.group(group)
                    //指定使用 NIO 的传输 Channel
                    .channel(NioServerSocketChannel.class)
                    //设置 socket 地址使用所选的端口
                    .localAddress(new InetSocketAddress(port))
                    //添加 EchoServerHandler 到 Channel 的 ChannelPipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            //绑定的服务器;sync 等待服务器关闭
            ChannelFuture future = bootstrap.bind().sync();
            System.out.println(EchoServer.class.getName() + " started and listen on " + future.channel().localAddress());
            //关闭 channel 和 块,直到它被关闭
            future.channel().closeFuture().sync();
        } finally {
            //关闭 EventLoopGroup,释放所有资源
            group.shutdownGracefully().sync();
        }
    }
}

4.EchoClientHandler

package com.hzm.myproject.netty.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/**
 * @Author:huangzhimin
 * @Date:2020/6/8
 * @描述:
 *  连接服务器
 *  发送信息
 *  发送的每个信息,等待和接收从服务器返回的同样的信息
 *  关闭连接
 **/
@ChannelHandler.Sharable  //@Sharable 标记这个类的实例可以在 channel 里共享
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    /**
     * 服务器的连接被建立后调用
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        //当被通知该 channel 是活动的时候就发送信息
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!---hzm", CharsetUtil.UTF_8));
    }

    /**
     * 数据后从服务器接收到调用
     * @param ctx
     * @param in
     */
    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
        //记录接收到的消息
        System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8));
    }

    /**
     * 捕获一个异常时调用
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        //记录日志错误并关闭 channel
        cause.printStackTrace();
        ctx.close();
    }
}

5.EchoClient

package com.hzm.myproject.netty.echo;

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

import java.net.InetSocketAddress;

/**
 * @Author:huangzhimin
 * @Date:2020/6/8
 * @描述:引导客户端
 **/
public class EchoClient {
    private final String host;
    private final int port;

    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //1创建 Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            //2指定 EventLoopGroup 来处理客户端事件。由于我们使用 NIO 传输,所以用到了 NioEventLoopGroup 的实现
            bootstrap.group(group)
                    //3使用的 channel 类型是一个用于 NIO 传输
                    .channel(NioSocketChannel.class)
                    //4设置服务器的 InetSocketAddress
                    .remoteAddress(new InetSocketAddress(host, port))
                    //5当建立一个连接和一个新的通道时,创建添加到 EchoClientHandler 实例 到 channel pipeline
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            //6 连接到远程;等待连接完成
            ChannelFuture future = bootstrap.connect().sync();
            //7 阻塞直到 Channel 关闭
            future.channel().closeFuture().sync();

        } finally {
            //8调用 shutdownGracefully() 来关闭线程池和释放所有资源
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
//        if (args.length != 2) {
//            System.err.println("Usage: " + EchoClient.class.getSimpleName() + " <host> <port>");
//            return;
//        }
        final String host = "0:0:0:0:0:0:0:0"; //服务器的路径
        final int port = Integer.parseInt("9999");//服务器的端口
        new EchoClient(host, port).start();
    }
}