Netty入门使用

112 阅读3分钟

Netty的介绍

Netty 是一个高性能、异步事件驱动的 NIO 框架,基于 JAVA NIO 提供的 API 实现。

Netty的使用

Netty的模板代码

netty开发基本上都是基于固定的模板代码进行开发

三行核心代码,
1. 初始化Bootstrap
2. 链式方式配置参数
3. 启动并绑定端口

业务处理会放在Handler里,可以自定义
  1. 服务端代码
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.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class NettyServer {
    public static void main(String[] args) {
        //创建两个线程组bossGroup和workGroup,含有的子线程NioEventLoopGroup的个数默认为cpu核数的两倍
        //bossGroup只是处理连接请求,真正的和客户端业务处理,会交给workGroup完成
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try{
            //创建一个ServerBootstrap,用于服务器引导辅助类
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用链式编程来配置参数
            bootstrap.group(bossGroup,workGroup) //设置两个线程组
                    //设置NioServerSocketChannel作为服务器的通道实现
                    .channel(NioServerSocketChannel.class)
                    //初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只处理一个客户端连接
                    //多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            //对workerGroup的SocketChannel设置处理器
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            System.out.println("netty sever start...");
            //绑定一个端口并且同步,生成一个ChannelFuture异步对象,通过isDone()方法判断异步方法执行情况
            //启动服务器,绑定端口,bind是异步绑定,sync()方法会阻塞等待直到绑定成功
            ChannelFuture future = bootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //关闭EventLoopGroup,释放掉所有资源
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}
  1. 自定义的Handler,这里实现对数据的处理
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.StandardCharsets;

/**
 * 自定义Handler需要继承netty规定好的某个HandlerAdapter(规范)
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 当客户端连接服务器完成就会触发该方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
    }

    /**
     * 当客户端发送消息到服务器时会触发该方法
     * @param ctx 上下文对象,含有通道channel,管道pipeline
     * @param msg 客户端发送的数据
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //将客户端发送的数据转换为ByteBuf,类似NIO的ByteBuffer
        ByteBuf buf = (ByteBuf) msg;
        //如果要发送String类型,需要在pipeline中加入字符串的编码解码器,可以继续调用监听回写成功与否的API
        ctx.channel().writeAndFlush(buf);
        System.out.println("服务器接收到的数据:" + buf.toString(StandardCharsets.UTF_8));
    }

    /**
     *  1.客户端发送关闭帧 2.客户端结束进程 3.服务端主动调用channel.close()时调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }
}
  1. 完成以上服务端代码可以使用telnet命令进行测试一下
  2. netty也有客户端的模板代码
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;

import java.net.InetSocketAddress;

public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        //创建启动器类
        new Bootstrap()
                //添加eventLoop
                .group(new NioEventLoopGroup())
                //选择客户端channel事件
                .channel(NioSocketChannel.class)
                //添加处理器
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override //在连接之后调用
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                //连接到服务器
                .connect(new InetSocketAddress("localhost",8080))
                .sync()//阻塞方法直到连接
                .channel()//代表连接对象
                //向服务器发送数据
                .writeAndFlush("hello world");
    }
}
  1. 解析协议时要在pipeline加入相应协议的解码器
  2. String转ByteBuf
ByteBuf buf = Unpooled.wrappedBuffer(bytes);