轉載請註明出處:http://www.cnblogs.com/Joanna-Yan/p/7814644.html html
前面講到:Netty(一)——Netty入門程序java
主要內容:bootstrap
TCP是個「流」協議,所謂流,就是沒有界限的一串數據。TCP底層並不瞭解上層業務數據的具體含義,它會根據TCP緩衝區的實際狀況進行包的劃分,因此在業務上認爲,一個完整的包可能會被TCP拆分紅多個包進行發送,也有可能把多個小的包封裝成一個大的數據包發送,這就是所謂的TCP粘包和拆包的問題。緩存
咱們經過下圖對TCP粘包和拆包問題進行說明:服務器
假設客戶端分別發送了兩個數據包D1和D2給服務端,因爲服務端一次讀取到的字節數是不肯定的,故可能存在如下四種狀況。微信
若是此時服務端TCP接收滑窗很是小,而數據包D1和D2比較大,極可能會發生第五種可能,即服務端分屢次才能將D1和D2包接收徹底,期間發生屢次拆包。網絡
問題產生的緣由有三個:框架
因爲底層的TCP沒法理解上層的業務數據,因此在底層是沒法保證數據包不被拆分和重組的,這個問題只能經過上層的應用協議棧設計來解決,根據業界的主流協議的解決方案,能夠概括以下。異步
下面咱們就經過實際示例來看看如何使用Netty提供的半包解碼器來解決TCP粘包/拆包問題。socket
在前面的時間服務器示例中,咱們屢次強調並無考慮讀半包問題,這在功能測試時每每沒有問題,可是一旦壓力上來,或者發送大報文以後,就會存在粘包/拆包問題。若是代碼沒有考慮,每每就會出現解碼錯位或者錯誤,致使程序不能正常工做。下面咱們之前面的Netty(一)——Netty入門程序爲例,模擬故障場景,而後看看如何正確使用Netty的半包解碼器來解決TCP粘包/拆包問題。
package joanna.yan.netty; 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.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; public class TimeServer { public static void main(String[] args) throws Exception { int port=9090; if(args!=null&&args.length>0){ try { port=Integer.valueOf(args[0]); } catch (Exception e) { // 採用默認值 } } new TimeServer().bind(port); } public void bind(int port) throws Exception{ /* * 配置服務端的NIO線程組,它包含了一組NIO線程,專門用於網絡事件的處理,實際上它們就是Reactor線程組。 * 這裏建立兩個的緣由:一個用於服務端接受客戶端的鏈接, * 另外一個用於進行SocketChannel的網絡讀寫。 */ EventLoopGroup bossGroup=new NioEventLoopGroup(); EventLoopGroup workerGroup=new NioEventLoopGroup(); try { //ServerBootstrap對象,Netty用於啓動NIO服務端的輔助啓動類,目的是下降服務端的開發複雜度。 ServerBootstrap b=new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) /* * 綁定I/O事件的處理類ChildChannelHandler,它的做用相似於Reactor模式中的handler類, * 主要用於處理網絡I/O事件,例如:記錄日誌、對消息進行編解碼等。 */ .childHandler(new ChildChannelHandler()); /* * 綁定端口,同步等待成功(調用它的bind方法綁定監聽端口,隨後,調用它的同步阻塞方法sync等待綁定操做完成。 * 完成以後Netty會返回一個ChannelFuture,它的功能相似於JDK的java.util.concurrent.Future, * 主要用於異步操做的通知回調。) */ ChannelFuture f=b.bind(port).sync(); //等待服務端監聽端口關閉(使用f.channel().closeFuture().sync()方法進行阻塞,等待服務端鏈路關閉以後main函數才退出。) f.channel().closeFuture().sync(); }finally{ //優雅退出,釋放線程池資源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel arg0) throws Exception { // arg0.pipeline().addLast(new TimeServerHandler()); //模擬粘包/拆包故障場景 arg0.pipeline().addLast(new TimeServerHandler1()); } } }
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * 用於對網絡事件進行讀寫操做 * 模擬粘包/拆包故障場景 * @author Joanna.Yan * @date 2017年11月8日下午6:54:35 */ public class TimeServerHandler1 extends ChannelInboundHandlerAdapter{ private int counter; @Override public 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, "UTF-8").substring(0, req.length-System.getProperty("line.separator").length()); System.out.println("The time server receive order : "+body+" ;the counter is :"+ ++counter); String currentTime="QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; currentTime=currentTime+System.getProperty("line.separator"); ByteBuf resp=Unpooled.copiedBuffer(currentTime.getBytes()); ctx.write(resp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
package joanna.yan.netty; import io.netty.bootstrap.Bootstrap; 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.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; public class TimeClient { public static void main(String[] args) throws Exception { int port=9090; if(args!=null&&args.length>0){ try { port=Integer.valueOf(args[0]); } catch (Exception e) { // 採用默認值 } } new TimeClient().connect(port, "127.0.0.1"); } public void connect(int port,String host) throws Exception{ //配置客戶端NIO線程組 EventLoopGroup group=new NioEventLoopGroup(); try { Bootstrap b=new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // ch.pipeline().addLast(new TimeClientHandler()); //模擬粘包/拆包故障場景 ch.pipeline().addLast(new TimeClientHandler1()); } }); //發起異步鏈接操做 ChannelFuture f=b.connect(host, port).sync(); //等待客戶端鏈路關閉 f.channel().closeFuture().sync(); }finally{ //優雅退出,釋放NIO線程組 group.shutdownGracefully(); } } }
package joanna.yan.netty; import java.util.logging.Logger; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * 模擬粘包/拆包故障場景 * @author Joanna.Yan * @date 2017年11月10日下午2:18:51 */ public class TimeClientHandler1 extends ChannelInboundHandlerAdapter{ private static final Logger logger=Logger.getLogger(TimeClientHandler1.class.getName()); private int counter; private byte[] req; public TimeClientHandler1(){ req=("QUER TIME ORDER"+System.getProperty("line.separator")).getBytes(); } @Override public 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); } } @Override public 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, "UTF-8"); System.out.println("Now is :"+body+" ;the counter is :" + ++counter); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //釋放資源 logger.warning("Unexpected exception from downstream : "+cause.getMessage()); ctx.close(); } }
分別執行服務端和客戶端,運行結果以下:
服務端運行結果以下:
The time server receive order : QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QU ;the counter is :1 The time server receive order : TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER QUER TIME ORDER ;the counter is :2
服務端運行結果代表它只接收到了兩條消息,總數正好是100條,咱們期待的是收到100條消息,每條包括一條「QUERY TIME ORDER」指令。這說明發生了TCP粘包。
客戶端運行結果以下:
Now is : BAD ORDER
BAD ORDER
; the counter is : 1
按照設計初衷,客戶端應該收到100條當前系統時間的消息,但實際上只收到了一條。這不難理解,由於服務端只收到了2條請求消息,因此實際服務端只發送了2條應答,因爲請求消息不知足查詢條件,因此只返回了2條「BAD ORDER」應答消息。可是實際上客戶端只收到了一條包含2條「BAD ORDER」指令的消息,說明服務端返回的應答消息也發送了粘包。
因爲上面的例程沒有考慮TCP的粘包/拆包,因此當發生TCP粘包時,咱們的程序就不能正常工做。
下面咱們經過Netty的LineBaseFrameDecoder和StringDecoder來解決TCP粘包問題。
爲了解決TCP粘包/拆包致使的半包讀寫問題,Netty默認提供了多種編解碼器用於處理半包,只要能熟練掌握這些類庫的使用,TCP粘包問題今後會變得很是容易,你甚至不須要關心它們,這也是其餘NIO框架和JDK原生的NIO API所沒法匹敵的。
下面咱們對時間服務進行修改。
package joanna.yan.netty.sp; 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.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class TimeServer { public static void main(String[] args) throws Exception { int port=9090; if(args!=null&&args.length>0){ try { port=Integer.valueOf(args[0]); } catch (Exception e) { // 採用默認值 } } new TimeServer().bind(port); } public void bind(int port) throws Exception{ //配置服務端的NIO線程組 EventLoopGroup bossGroup=new NioEventLoopGroup(); EventLoopGroup workerGroup=new NioEventLoopGroup(); try { ServerBootstrap b=new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChildChannelHandler()); //綁定端口,同步等待成功 ChannelFuture f=b.bind(port).sync(); //等待服務端監聽端口關閉 f.channel().closeFuture().sync(); }finally{ //優雅退出,釋放線程池資源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel arg0) throws Exception { arg0.pipeline().addLast(new LineBasedFrameDecoder(1024)); arg0.pipeline().addLast(new StringDecoder()); arg0.pipeline().addLast(new TimeServerHandler()); } } }
package joanna.yan.netty.sp; import java.util.Date; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class TimeServerHandler extends ChannelInboundHandlerAdapter{ private int counter; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body=(String) msg; System.out.println("The time server receive order : "+body+" ;the counter is : "+ ++counter); String currentTime="QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; currentTime =currentTime+System.getProperty("line.separator"); ByteBuf resp=Unpooled.copiedBuffer(currentTime.getBytes()); ctx.writeAndFlush(resp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
package joanna.yan.netty.sp; import io.netty.bootstrap.Bootstrap; 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.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class TimeClient { public static void main(String[] args) throws Exception { int port=9090; if(args!=null&&args.length>0){ try { port=Integer.valueOf(args[0]); } catch (Exception e) { // 採用默認值 } } new TimeClient().connect(port, "127.0.0.1"); } public void connect(int port,String host) throws Exception{ //配置客戶端NIO線程組 EventLoopGroup group=new NioEventLoopGroup(); try { Bootstrap b=new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //直接在TimeClientHandler以前新增LineBasedFrameDecoder和StringDecoder解碼器 ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new TimeClientHandler()); } }); //發起異步鏈接操做 ChannelFuture f=b.connect(host, port).sync(); //等待客戶端鏈路關閉 f.channel().closeFuture().sync(); }finally{ //優雅退出,釋放NIO線程組 group.shutdownGracefully(); } } }
package joanna.yan.netty.sp; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.logging.Logger; public class TimeClientHandler extends ChannelInboundHandlerAdapter{ private static final Logger logger=Logger.getLogger(TimeClientHandler.class.getName()); private int counter; private byte[] req; public TimeClientHandler(){ req=("QUERY TIME ORDER"+System.getProperty("line.separator")).getBytes(); } @Override public 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); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //拿到的msg已是解碼成字符串以後的應答消息了,相比於以前的代碼簡潔明瞭不少。 String body=(String) msg; System.out.println("Now is :"+body+" ;the counter is :" + ++counter); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //釋放資源 logger.warning("Unexpected exception from downstream : "+cause.getMessage()); ctx.close(); } }
爲了儘可能模擬TCP粘包和半包場景,採用簡單的壓力測試,鏈路創建成功以後,客戶端連續發送100條消息給服務端,而後查看服務端和客戶端的運行結果。
服務端執行結果以下:
The time server receive order : QUER TIME ORDER ;the counter is : 1 The time server receive order : QUER TIME ORDER ;the counter is : 2 The time server receive order : QUER TIME ORDER ;the counter is : 3 The time server receive order : QUER TIME ORDER ;the counter is : 4 The time server receive order : QUER TIME ORDER ;the counter is : 5 The time server receive order : QUER TIME ORDER ;the counter is : 6 The time server receive order : QUER TIME ORDER ;the counter is : 7 The time server receive order : QUER TIME ORDER ;the counter is : 8 The time server receive order : QUER TIME ORDER ;the counter is : 9 The time server receive order : QUER TIME ORDER ;the counter is : 10 The time server receive order : QUER TIME ORDER ;the counter is : 11 The time server receive order : QUER TIME ORDER ;the counter is : 12 The time server receive order : QUER TIME ORDER ;the counter is : 13 The time server receive order : QUER TIME ORDER ;the counter is : 14 The time server receive order : QUER TIME ORDER ;the counter is : 15 The time server receive order : QUER TIME ORDER ;the counter is : 16 The time server receive order : QUER TIME ORDER ;the counter is : 17 The time server receive order : QUER TIME ORDER ;the counter is : 18 The time server receive order : QUER TIME ORDER ;the counter is : 19 The time server receive order : QUER TIME ORDER ;the counter is : 20 The time server receive order : QUER TIME ORDER ;the counter is : 21 The time server receive order : QUER TIME ORDER ;the counter is : 22 The time server receive order : QUER TIME ORDER ;the counter is : 23 The time server receive order : QUER TIME ORDER ;the counter is : 24 The time server receive order : QUER TIME ORDER ;the counter is : 25 The time server receive order : QUER TIME ORDER ;the counter is : 26 The time server receive order : QUER TIME ORDER ;the counter is : 27 The time server receive order : QUER TIME ORDER ;the counter is : 28 The time server receive order : QUER TIME ORDER ;the counter is : 29 The time server receive order : QUER TIME ORDER ;the counter is : 30 The time server receive order : QUER TIME ORDER ;the counter is : 31 The time server receive order : QUER TIME ORDER ;the counter is : 32 The time server receive order : QUER TIME ORDER ;the counter is : 33 The time server receive order : QUER TIME ORDER ;the counter is : 34 The time server receive order : QUER TIME ORDER ;the counter is : 35 The time server receive order : QUER TIME ORDER ;the counter is : 36 The time server receive order : QUER TIME ORDER ;the counter is : 37 The time server receive order : QUER TIME ORDER ;the counter is : 38 The time server receive order : QUER TIME ORDER ;the counter is : 39 The time server receive order : QUER TIME ORDER ;the counter is : 40 The time server receive order : QUER TIME ORDER ;the counter is : 41 The time server receive order : QUER TIME ORDER ;the counter is : 42 The time server receive order : QUER TIME ORDER ;the counter is : 43 The time server receive order : QUER TIME ORDER ;the counter is : 44 The time server receive order : QUER TIME ORDER ;the counter is : 45 The time server receive order : QUER TIME ORDER ;the counter is : 46 The time server receive order : QUER TIME ORDER ;the counter is : 47 The time server receive order : QUER TIME ORDER ;the counter is : 48 The time server receive order : QUER TIME ORDER ;the counter is : 49 The time server receive order : QUER TIME ORDER ;the counter is : 50 The time server receive order : QUER TIME ORDER ;the counter is : 51 The time server receive order : QUER TIME ORDER ;the counter is : 52 The time server receive order : QUER TIME ORDER ;the counter is : 53 The time server receive order : QUER TIME ORDER ;the counter is : 54 The time server receive order : QUER TIME ORDER ;the counter is : 55 The time server receive order : QUER TIME ORDER ;the counter is : 56 The time server receive order : QUER TIME ORDER ;the counter is : 57 The time server receive order : QUER TIME ORDER ;the counter is : 58 The time server receive order : QUER TIME ORDER ;the counter is : 59 The time server receive order : QUER TIME ORDER ;the counter is : 60 The time server receive order : QUER TIME ORDER ;the counter is : 61 The time server receive order : QUER TIME ORDER ;the counter is : 62 The time server receive order : QUER TIME ORDER ;the counter is : 63 The time server receive order : QUER TIME ORDER ;the counter is : 64 The time server receive order : QUER TIME ORDER ;the counter is : 65 The time server receive order : QUER TIME ORDER ;the counter is : 66 The time server receive order : QUER TIME ORDER ;the counter is : 67 The time server receive order : QUER TIME ORDER ;the counter is : 68 The time server receive order : QUER TIME ORDER ;the counter is : 69 The time server receive order : QUER TIME ORDER ;the counter is : 70 The time server receive order : QUER TIME ORDER ;the counter is : 71 The time server receive order : QUER TIME ORDER ;the counter is : 72 The time server receive order : QUER TIME ORDER ;the counter is : 73 The time server receive order : QUER TIME ORDER ;the counter is : 74 The time server receive order : QUER TIME ORDER ;the counter is : 75 The time server receive order : QUER TIME ORDER ;the counter is : 76 The time server receive order : QUER TIME ORDER ;the counter is : 77 The time server receive order : QUER TIME ORDER ;the counter is : 78 The time server receive order : QUER TIME ORDER ;the counter is : 79 The time server receive order : QUER TIME ORDER ;the counter is : 80 The time server receive order : QUER TIME ORDER ;the counter is : 81 The time server receive order : QUER TIME ORDER ;the counter is : 82 The time server receive order : QUER TIME ORDER ;the counter is : 83 The time server receive order : QUER TIME ORDER ;the counter is : 84 The time server receive order : QUER TIME ORDER ;the counter is : 85 The time server receive order : QUER TIME ORDER ;the counter is : 86 The time server receive order : QUER TIME ORDER ;the counter is : 87 The time server receive order : QUER TIME ORDER ;the counter is : 88 The time server receive order : QUER TIME ORDER ;the counter is : 89 The time server receive order : QUER TIME ORDER ;the counter is : 90 The time server receive order : QUER TIME ORDER ;the counter is : 91 The time server receive order : QUER TIME ORDER ;the counter is : 92 The time server receive order : QUER TIME ORDER ;the counter is : 93 The time server receive order : QUER TIME ORDER ;the counter is : 94 The time server receive order : QUER TIME ORDER ;the counter is : 95 The time server receive order : QUER TIME ORDER ;the counter is : 96 The time server receive order : QUER TIME ORDER ;the counter is : 97 The time server receive order : QUER TIME ORDER ;the counter is : 98 The time server receive order : QUER TIME ORDER ;the counter is : 99 The time server receive order : QUER TIME ORDER ;the counter is : 100
客戶端執行結果以下:
Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :1 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :2 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :3 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :4 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :5 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :6 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :7 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :8 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :9 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :10 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :11 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :12 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :13 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :14 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :15 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :16 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :17 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :18 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :19 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :20 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :21 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :22 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :23 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :24 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :25 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :26 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :27 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :28 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :29 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :30 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :31 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :32 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :33 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :34 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :35 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :36 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :37 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :38 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :39 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :40 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :41 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :42 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :43 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :44 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :45 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :46 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :47 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :48 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :49 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :50 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :51 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :52 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :53 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :54 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :55 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :56 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :57 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :58 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :59 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :60 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :61 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :62 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :63 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :64 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :65 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :66 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :67 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :68 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :69 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :70 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :71 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :72 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :73 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :74 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :75 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :76 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :77 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :78 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :79 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :80 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :81 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :82 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :83 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :84 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :85 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :86 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :87 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :88 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :89 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :90 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :91 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :92 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :93 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :94 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :95 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :96 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :97 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :98 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :99 Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :100
程序的運行結果徹底符合預期,說明經過使用LineBasedFrameDecoder和StringDecoder成功解決了TCP粘包致使的讀半包問題。對於使用者來講,只要將支持半包解碼的handler添加到ChannelPipeline中便可,不須要寫額外的代碼,用戶使用起來很是簡單。
LineBasedFrameDecoder的工做原理是它依次遍歷ByteBuf中的可讀字節,判斷看是否有"\n"或者「\r\n」,若是有,就以此位置爲結束位置,從可讀索引到結束位置區間的字節就組成了一行。它是以換行符爲結束標誌的解碼器,支持攜帶結束符或者不攜帶結束符兩種解碼方式。同時支持配置單行的最大長度。若是連續讀取到的最大長度後仍沒有發現換行符,就會拋出異常,同時忽略掉以前督導的異常碼流。
StringDecoder的功能很是簡單,就是將收到到的對象轉換成字符串,而後繼續調用後面的handler。LineBasedFrameDecoder+StringDecoder組合就是按行切換的文本解碼器,它被設計用來支持TCP的粘包和拆包。
疑問:若是發送的消息不是以換行符結束的該怎麼辦呢?或者沒有回車換行符,靠消息頭中的長度字段來分包怎麼辦?是否是須要本身寫半包解碼器?答案是否認的,Netty提供了多種支持TCP粘包/拆包的解碼器,用來知足用戶的不一樣訴求。
若是此文對您有幫助,微信打賞我一下吧~