Netty提供的TCP数据拆包、粘包解决方案

1.前言

关于TCP的数据拆包、粘包的介绍,我在上一篇文章里面已经有过介绍。

想要了解一下的,请点击这里 Chick Here!

今天我们要讲解的是Netty提供的两种解决方案:

  1. DelimiterBasedFrameDecoder
  2. FixedLengthFrameDecoder

2.关于Decoder

  1. 先观察下两段代码的不同

    (1)使用StringDecoder之前

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { ByteBuf in = (ByteBuf) msg;
    String str = in.toString(CharsetUtil.UTF_8);
    System.out.println("Client:"+str); } finally {
    ReferenceCountUtil.release(msg);
    }
    }

    (2)使用StringDecoder之后

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try {
    String str = (String) msg;
    System.out.println("Client:"+str); } finally {
    ReferenceCountUtil.release(msg);
    }
    }
  2. 关于Decoder

    decoder:n. 解码器

    在我看来,Netty数据的解析方式大概为:

    发送过程:Buffer------>数据报------>比特流

    接受过程:Buffer<------数据报<------比特流

    所以我们接受到的msg是一个ButeBuf

    使用了Decoder(这里使用StringDecoder举例)之后:

    发送过程:Buffer------>数据报------>比特流

    接受过程:String<------Buffer<------数据报<------比特流

    相当于ByteBuf按照StringDecoder的解码规则,把msg翻译成为了一个字符串。

  3. 如何使用Decoder

    (1)实际代码演示:

    package com.xm.netty.demo02;
    
    import java.net.InetSocketAddress;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.string.StringDecoder; public class Server { private final int port; public Server(int port) {
    this.port = port;
    } public static void main(String[] args) { int port = 8989;
    try {
    new Server(port).start();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } } private void start() throws InterruptedException {
    EventLoopGroup g1 = new NioEventLoopGroup();
    EventLoopGroup g2 = new NioEventLoopGroup();
    try {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap
    .group(g1,g2)
    .channel(NioServerSocketChannel.class)
    .localAddress(new InetSocketAddress( port))
    .childHandler(new ChannelInitializer() {
    @Override
    protected void initChannel(Channel ch) throws Exception {
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(new ServerHandler());
    }
    });
    ChannelFuture future = bootstrap.bind().sync();
    future.channel().closeFuture().sync();
    } finally {
    g1.shutdownGracefully().sync();
    g2.shutdownGracefully().sync();
    }
    } }

    代码改动:

    ch.pipeline().addLast(new StringDecoder());

    ​ ch.pipeline().addLast(new ServerHandler());

    (2)多个Decoder的使用顺序:

    从前往后,依次解码

    假设我们有个通过字符串变化为时间的TimeDecoder:

    ch.pipeline().addLast(new StringDecoder());

    ch.pipeline().addLast(new TimeDecoder());

    ​ ch.pipeline().addLast(new ServerHandler());

    解析规则为:

3.DelimiterBasedFrameDecoder

  1. 关于DelimiterBasedFrameDecoder

    其实很简单,就是在一个缓冲区的末尾添加一个结束字符。

    在规定了最大长度的缓冲区里,遇到一个特殊字符,就截取一次。

    原理类似于String的split()方法。

  2. 代码实现

    (1)服务端Server

    package com.xm.netty.demo03;
    
    import java.net.InetSocketAddress;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.DelimiterBasedFrameDecoder;
    import io.netty.handler.codec.string.StringDecoder; public class Server { private final int port; public Server(int port) {
    this.port = port;
    } public static void main(String[] args) { int port = 8989;
    try {
    new Server(port).start();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } } private void start() throws InterruptedException {
    EventLoopGroup g1 = new NioEventLoopGroup();
    EventLoopGroup g2 = new NioEventLoopGroup();
    try {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap
    .group(g1,g2)
    .channel(NioServerSocketChannel.class)
    .localAddress(new InetSocketAddress( port))
    .childHandler(new ChannelInitializer() {
    @Override
    protected void initChannel(Channel ch) throws Exception {
    ByteBuf buf = Unpooled.copiedBuffer("$".getBytes());
    ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(new ServerHandler());
    }
    });
    ChannelFuture future = bootstrap.bind().sync();
    future.channel().closeFuture().sync();
    } finally {
    g1.shutdownGracefully().sync();
    g2.shutdownGracefully().sync();
    }
    } }

    (2)服务端ServerHandler

    package com.xm.netty.demo03;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter; import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.CharsetUtil; public class ServerHandler extends ChannelHandlerAdapter { @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String str = (String) msg;
    System.out.println("Server:"+str);
    str = "服务器返回--->"+ str+"$";
    ctx.writeAndFlush(Unpooled.copiedBuffer(str.getBytes()));
    } @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    } @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())+"一个客户端连接上服务器!");
    } }

    (3)客户端Client

    package com.xm.netty.demo03;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter; import io.netty.bootstrap.Bootstrap;
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    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.DelimiterBasedFrameDecoder;
    import io.netty.handler.codec.string.StringDecoder; public class Client { private final int port;
    private final String host; public Client(int port, String host) {
    this.port = port;
    this.host = host;
    } public static void main(String[] args) {
    String host = "127.0.0.1";
    int port = 8989;
    try {
    new Client(port, host).start();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    } private void start() throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup();
    try {
    Bootstrap bootstrap = new Bootstrap();
    bootstrap
    .group(group)
    .channel(NioSocketChannel.class)
    .remoteAddress(host, port)
    .handler(new ChannelInitializer<SocketChannel>() { @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    ByteBuf buf = Unpooled.copiedBuffer("$".getBytes());
    ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(new ClientHandler());
    } }); ChannelFuture future = bootstrap.connect().sync(); for(int i=10;i<20;i++) {
    String str = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now()) + "---- " +i+"<<<$";
    future.channel().write(Unpooled.copiedBuffer(str.getBytes()));
    } future.channel().flush(); //future.channel().writeAndFlush(Unpooled.copiedBuffer("Hello Netty!".getBytes())); future.channel().closeFuture().sync();
    } finally {
    group.shutdownGracefully().sync();
    } } }

    (4)客户端ClientHandler

    package com.xm.netty.demo03;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter; import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.CharsetUtil;
    import io.netty.util.ReferenceCountUtil; public class ClientHandler extends ChannelHandlerAdapter { @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try {
    String str = (String) msg;
    System.out.println("Client:"+str); } finally {
    ReferenceCountUtil.release(msg);
    }
    } @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    } @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())+"已连接服务器!");
    } }
  3. 运行结果截图

    (1)服务端运行结果:

    (2)客户端运行结果:

4.FixedLengthFrameDecoder

  1. 关于FixedLengthFrameDecoder

    其实很简单,就是对规定的发送的数据进行限制长度,

    当符合这个长度的情况下,就可以解析。

    假设你发送一个’123456‘,’654321‘

    那么解析的状况为’12345‘,’66543‘

  2. 代码实现

    (1)服务端Server

    package com.xm.netty.demo04;
    
    import java.net.InetSocketAddress;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.FixedLengthFrameDecoder;
    import io.netty.handler.codec.string.StringDecoder; public class Server { private final int port; public Server(int port) {
    this.port = port;
    } public static void main(String[] args) { int port = 8989;
    try {
    new Server(port).start();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } } private void start() throws InterruptedException {
    EventLoopGroup g1 = new NioEventLoopGroup();
    EventLoopGroup g2 = new NioEventLoopGroup();
    try {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap
    .group(g1,g2)
    .channel(NioServerSocketChannel.class)
    .localAddress(new InetSocketAddress( port))
    .childHandler(new ChannelInitializer() {
    @Override
    protected void initChannel(Channel ch) throws Exception {
    ch.pipeline().addLast(new FixedLengthFrameDecoder(5));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(new ServerHandler());
    }
    });
    ChannelFuture future = bootstrap.bind().sync();
    future.channel().closeFuture().sync();
    } finally {
    g1.shutdownGracefully().sync();
    g2.shutdownGracefully().sync();
    }
    } }

    (2)服务端ServerHandler

    package com.xm.netty.demo04;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter; import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.CharsetUtil; public class ServerHandler extends ChannelHandlerAdapter { @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String str = (String) msg;
    System.out.println("Server:"+str);
    ctx.writeAndFlush(Unpooled.copiedBuffer(str.getBytes()));
    } @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    } @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())+"一个客户端连接上服务器!");
    } }

    (3)客户端Client

    package com.xm.netty.demo04;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter; import io.netty.bootstrap.Bootstrap;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    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.FixedLengthFrameDecoder;
    import io.netty.handler.codec.string.StringDecoder; public class Client { private final int port;
    private final String host; public Client(int port, String host) {
    this.port = port;
    this.host = host;
    } public static void main(String[] args) {
    String host = "127.0.0.1";
    int port = 8989;
    try {
    new Client(port, host).start();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    } private void start() throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup();
    try {
    Bootstrap bootstrap = new Bootstrap();
    bootstrap
    .group(group)
    .channel(NioSocketChannel.class)
    .remoteAddress(host, port)
    .handler(new ChannelInitializer<SocketChannel>() { @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(new FixedLengthFrameDecoder(5));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(new ClientHandler());
    } }); ChannelFuture future = bootstrap.connect().sync(); for(int i=123450;i<123460;i++) {
    String str = ""+i;
    future.channel().write(Unpooled.copiedBuffer(str.getBytes()));
    }
    future.channel().flush(); //future.channel().writeAndFlush(Unpooled.copiedBuffer("Hello Netty!".getBytes())); future.channel().closeFuture().sync();
    } finally {
    group.shutdownGracefully().sync();
    } } }

    (4)客户端ClientHandler

    package com.xm.netty.demo04;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter; import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.CharsetUtil;
    import io.netty.util.ReferenceCountUtil; public class ClientHandler extends ChannelHandlerAdapter { @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try {
    String str = (String) msg;
    System.out.println("Client:"+str); } finally {
    ReferenceCountUtil.release(msg);
    }
    } @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    } @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())+"已连接服务器!");
    } }
  3. 运行结果截图

    (1)服务端运行结果:

    (2)客户端运行结果:

3.Netty的粘包、拆包(二)的更多相关文章

  1. Netty TCP粘包/拆包问题《二》

    1.DelimiterBasedFrameDecoder:是以分隔符作为结束标志进行解决粘包/拆包问题 代码: EchoClient:客户端 /* * Copyright 2012 The Netty ...

  2. netty 解决粘包拆包问题

    netty server TimeServer package com.zhaowb.netty.ch4_3; import io.netty.bootstrap.ServerBootstrap; i ...

  3. Netty TCP粘包/拆包问题《一》

    1.使用LineBasedFrameDecoder,StringDecoder解析器进行解决TCP粘包/拆包问题 2.代码搞起: TimeClient:客户端 /* * Copyright 2013- ...

  4. netty: 解决粘包拆包: 分隔符DelimiterBasedFrameDecoder,定长消息FixedLengthFrameDecoder

    DelimiterBasedFrameDecoder 自定义分隔符 给Server发送多条信息,但是server会讲多条信息合并为一条.这时候我们需要对发生的消息指定分割,让client和server ...

  5. Netty入门系列(2) --使用Netty解决粘包和拆包问题

    前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...

  6. 《精通并发与Netty》学习笔记(14 - 解决TCP粘包拆包(二)Netty自定义协议解决粘包拆包)

    一.Netty粘包和拆包解决方案 Netty提供了多个解码器,可以进行分包的操作,分别是: * LineBasedFrameDecoder (换行)   LineBasedFrameDecoder是回 ...

  7. Netty(二)——TCP粘包/拆包

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...

  8. 1. Netty解决Tcp粘包拆包

    一. TCP粘包问题 实际发送的消息, 可能会被TCP拆分成很多数据包发送, 也可能把很多消息组合成一个数据包发送 粘包拆包发生的原因 (1) 应用程序一次写的字节大小超过socket发送缓冲区大小 ...

  9. TCP粘包/拆包 ByteBuf和channel 如果没有Netty? 传统的多线程服务器,这个也是Apache处理请求的模式

    通俗地讲,Netty 能做什么? - 知乎 https://www.zhihu.com/question/24322387 谢邀.netty是一套在java NIO的基础上封装的便于用户开发网络应用程 ...

  10. 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)

    一.粘包/拆包概念 TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认 ...

随机推荐

  1. 17965 幸运之星(优先做) 约瑟夫环问题O(n)

    17965 幸运之星(优先做) 时间限制:100MS  内存限制:65535K 提交次数:0 通过次数:0 题型: 编程题   语言: G++;GCC;VC Description 每年新年派对的最后 ...

  2. OpenStack概念

    OpenStack is a global collaboration ofdevelopers and cloud computing technologists producing the ubi ...

  3. CSS选择器比较:queryselector queryselectorall

    官网解释: querySelector() and querySelectorAll() are two JavaScript functions very useful when working w ...

  4. golang精华资源

    转载自:http://blog.csdn.net/songbohr/article/details/13292261 1.Learning Go <学习Go语言> http://www.m ...

  5. Discuz论坛UCenter无法登录问题修复方法完美解决无限刷新问题

    级20141225版本后,出现UC无法登录问题[完美]解决方法.自圣诞节发布 Discuz! X3.2 R20141225  小编手头多个站点也都在升级后出现了,后台登录[ UCenter]假死情况, ...

  6. Swift-数组

    1.数组的定义  //OC 使用[]定义数组,Swift一样,但是没有@ //自动推导的结果[String]->表示数组中存的都是String //跟OC中的数组指定泛型类型  //Swift ...

  7. Flask文件目录----- db文件

    import sqlite3 import click from flask import current_app, g from flask.cli import with_appcontext d ...

  8. vue-devtools插件安装

    1.git clone https://github.com/vuejs/vue-devtools.git 2.此时表示vue-devtools包下载完成,进入到对应的目录下cd vue-devtoo ...

  9. AIR Native Extension for iOS 接入第三方sdk 如何实现 AppDelegate 生命周期

    作者:Panda Fang 出处:http://www.cnblogs.com/lonkiss/p/6492385.html 原创文章,转载请注明作者和出处,未经允许不可用于商业营利活动 去年到今年做 ...

  10. nvd3基于时间轴流程图

    doc http://nvd3-community.github.io/nvd3/examples/documentation.html https://github.com/mbostock/d3/ ...