网络IO总体上分为( 这里 的比喻不错):
- 阻塞
- 非阻塞
阻塞的方式写起来很简单:当链接可读的时候就读一些,不可读的时候就等待:
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好用一些 |