上一章记录了Java NIO简单使用,其中提到了常见的几种IO多路复用模型,本章我们初试Netty,使用Netty写一个Netty客户端与服务端连接。简单的比较一下Java NIO与Netty NIO的区别。
不同的IO模型对比
之前我们使用多线程优化过BIO模型,也使用过原生的NIO,这里我们使用表格对比下几种IO模型及区
对比项 |
同步阻塞IO |
伪异步IO |
非阻塞IO |
异步IO |
连接数:线程数 |
1:1 |
M:N |
M:1 |
M:0(无需IO线程,被动回调) |
Row2 |
阻塞同步IO |
阻塞同步 |
非阻塞同步 |
非阻塞异步 |
吞吐量 |
低 |
中 |
高 |
高 |
下面我们使用Netty进行最简单的NIO开发。
Netty服务端开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class NettyServer {
public void run(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class NettyServerHandler extends ChannelInboundHandlerAdapter {
Logger logger = Logger.getLogger(NettyServerHandler.class.getName());
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String readMsg = new String(bytes, StandardCharsets.UTF_8); logger.info("服务端接收到消息: "+readMsg); ReferenceCountUtil.release(msg); ByteBuf writeBuf = Unpooled.copiedBuffer((readMsg+"你好").getBytes(StandardCharsets.UTF_8)); ctx.writeAndFlush(writeBuf); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); logger.info("服务端发生异常关闭连接"); } }
|
Netty客户端开发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class NettyClient {
public void run(String host,int port){ EventLoopGroup ioGroup = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(ioGroup) .channel(NioSocketChannel.class) .option(NioChannelOption.TCP_NODELAY,true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyClientHandler()); } }); try { ChannelFuture future = b.connect(host,port).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { ioGroup.shutdownGracefully(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class NettyClientHandler extends ChannelInboundHandlerAdapter { Logger logger = Logger.getLogger(NettyClientHandler.class.getName()); private ByteBuf buf;
public NettyClientHandler() { }
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { sendMsg(ctx); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf readBuf = (ByteBuf) msg; byte[] data = new byte[readBuf.readableBytes()]; readBuf.readBytes(data); String readMsg = new String(data,StandardCharsets.UTF_8); logger.info("客户端接收到消息: "+readMsg); ReferenceCountUtil.release(msg); sendMsg(ctx); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); logger.info("客户端发生异常关闭连接..."); }
public void sendMsg(ChannelHandlerContext ctx){ String msg = "张三"; byte[] sendData = msg.getBytes(StandardCharsets.UTF_8); buf = Unpooled.buffer(sendData.length); buf.writeBytes(sendData); if (ctx!=null) { ctx.writeAndFlush(buf); } } }
|
测试


从上面可以看出,使用Netty开发高并发的服务器是比较快捷高效的。但是仍然没有处理TCP的粘包与拆包。下一节我们模拟下TCP的半包读写以及如何处理这些问题。