@Yano
2017-08-19T07:22:44.000000Z
字数 9221
阅读 2625
Netty
系列文章:http://www.jianshu.com/p/594441fb9c9e
本文完全参考自《Netty权威指南(第2版)》,李林峰著。
例程使用Maven构建工程,在pom文件中,加入Netty的依赖。
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.14.Final</version>
</dependency>
public class TimeServer {public void bind(int port) throws Exception {// 配置服务端的NIO线程EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChildChannelHandler());// 绑定端口,同步等待成功ChannelFuture f = bootstrap.bind(port).sync();// 等待服务端监听端口关闭f.channel().closeFuture().sync();} finally {// 优雅退出,释放线程池资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel arg0) throws Exception {arg0.pipeline().addLast(new TimeServerHandler());}}public static void main(String[] args) throws Exception {int port = 8080;new TimeServer().bind(port);}}
public class TimeServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, Charsets.UTF_8);System.out.println("The time server receive order : " + body);String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date().toString() : "BAD ORDER";ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());ctx.write(resp);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
流程:
public class TimeClient {public void connect(int port, String host) throws Exception {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new TimeClientHandler());}});// 发起异步连接操作ChannelFuture future = bootstrap.connect(host, port).sync();// 等待客户端链路关闭future.channel().closeFuture().sync();} finally {// 优雅退出,释放NIO线程组group.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8080;new TimeClient().connect(port, "127.0.0.1");}}
public class TimeClientHandler extends ChannelInboundHandlerAdapter {private final ByteBuf message;public TimeClientHandler() {byte[] req = "QUERY TIME ORDER".getBytes();message = Unpooled.buffer(req.length);message.writeBytes(req);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(message);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, Charsets.UTF_8);System.out.println("Now is : " + body);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
启动后,客户端输出:
Now is : Sat Aug 19 13:41:54 CST 2017
服务端输出:
The time server receive order : QUERY TIME ORDER
TCP是个“流”协议,流就是没有界限的一串数据。TCP底层并不了解业务上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分。所以在业务上,一个完整的包可能会被TCP拆成多个包发送,也有可能把多个小的包封装成一个大包发送。
因为底层的TCP无法理解上层的业务数据,所以只能通过上层的应用协议栈来解决这个问题,主要的解决方案有:
服务端:
public class TimeServerHandler extends ChannelInboundHandlerAdapter {private int counter;@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, Charsets.UTF_8).substring(0,req.length - System.getProperty("line.separator").length());System.out.println("count = " + ++counter);System.out.println("The time server receive order : " + body);String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date().toString() : "BAD ORDER";ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());ctx.write(resp);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
客户端:
public class TimeClientHandler extends ChannelInboundHandlerAdapter {private byte[] req;private int counter;public TimeClientHandler() {req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ByteBuf message = null;for (int i = 0; i < 100; i++) {message = Unpooled.buffer(req.length);message.writeBytes(req);ctx.writeAndFlush(message);}}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String body = new String(req, Charsets.UTF_8);System.out.println("Now is : " + body + ", the counter is " + ++counter);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}
TimeServer和TimeClient中,都增加下面的代码:
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));arg0.pipeline().addLast(new StringDecoder());
ServerHandler和ClientServer中,都将msg直接变成String。
服务端:
public class TimeServer {public void bind(int port) throws Exception {// 配置服务端的NIO线程EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChildChannelHandler());// 绑定端口,同步等待成功ChannelFuture f = bootstrap.bind(port).sync();// 等待服务端监听端口关闭f.channel().closeFuture().sync();} finally {// 优雅退出,释放线程池资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel arg0) throws Exception {arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));arg0.pipeline().addLast(new StringDecoder());arg0.pipeline().addLast(new TimeServerHandler());}}public static void main(String[] args) throws Exception {int port = 8080;new TimeServer().bind(port);}}
public class TimeServerHandler extends ChannelInboundHandlerAdapter {private int counter;@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String body = (String) msg;System.out.println("count = " + ++counter);System.out.println("The time server receive order : " + body);String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date().toString() : "BAD ORDER";ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());ctx.write(resp);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
客户端:
public class TimeClient {public void connect(int port, String host) {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LineBasedFrameDecoder(1024));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new TimeClientHandler());}});// 发起异步连接操作ChannelFuture future = bootstrap.connect(host, port).sync();// 等待客户端链路关闭future.channel().closeFuture().sync();} catch (Exception e) {e.printStackTrace();} finally {// 优雅退出,释放NIO线程组group.shutdownGracefully();}}public static void main(String[] args) throws Exception {int port = 8080;new TimeClient().connect(port, "127.0.0.1");}}
public class TimeClientHandler extends ChannelInboundHandlerAdapter {private byte[] req;private int counter;public TimeClientHandler() {req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ByteBuf message = null;for (int i = 0; i < 100; i++) {message = Unpooled.buffer(req.length);message.writeBytes(req);ctx.writeAndFlush(message);}}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String body = (String) msg;System.out.println("Now is : " + body + ", the counter is " + ++counter);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}