WsztRush

NETTY基础知识

网络IO总体上分为(这里的比喻不错):

  1. 阻塞
  2. 非阻塞

阻塞的方式写起来很简单:当链接可读的时候就读一些,不可读的时候就等待:

ServerSocket serverSocket = new ServerSocket(8787);
while (true) {
    Socket socket = serverSocket.accept();
    // TODO 交给线程池进行处理。
}

网络情况不好时阻塞的方式用起来有点蠢,用NIO(有点像SELECT/EPOLL)会靠谱些,当有链接可读时让工作线程来拿数据:

Selector selector = Selector.open();

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 8787));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select();
    Set<SelectionKey> selectionKeySet = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectionKeySet.iterator();
    while (iterator.hasNext()) {
        SelectionKey selectionKey = iterator.next();
        if (selectionKey.isAcceptable()) {
            ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
            SocketChannel socketChannel = channel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
        }
        if (selectionKey.isReadable()) {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int size = socketChannel.read(byteBuffer);
            if (size < 0) {
                selectionKey.cancel();
                socketChannel.close();
            }
            for (int i = 0; i < size; i++) {
                System.out.print((char) byteBuffer.get(i));
            }
        }
        iterator.remove();
    }
}

写最简单的功能都要这么多代码,维护起来也比较痛苦,下面来看如何用NETTY简化开发!

用法

下面的代码用来实现上面的功能:

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        ByteBuf buffer = (ByteBuf) msg;
                        int size = buffer.readableBytes();
                        for (int i = 0; i < size; i++) {
                            System.out.print((char) buffer.getByte(i));
                        }
                    }
                });
            }
        });
ChannelFuture future = bootstrap.bind(8787).sync();
future.channel().closeFuture().sync();

看起来也不怎么直观,不要急,先来了解一些NETTY中的概念:

概念 含义
Bootstrap/ServerBootstrap 配置netty(添加组件、设置参数)
Channel 定义I/O操作
ChannelHandlerContext  
ChannelHandler 处理感兴趣的事件(read、readomplete、bind、flush等)
ChannelPipeline ChannelHandler的容器
EventLoop/EventLoopGroup  
Future/Promise  
Unsafe  
ByteBuf 处理缓存的工具,比byte[]或者java.nio.ByteBuffer好用一些