zoukankan      html  css  js  c++  java
  • TCP--粘包拆包,netty的解决方式

    TCP基于链接的协议,并且保证有序性。

    但是,每个包的长度,需要明确,否则会发生粘包现象。

    以下示例为一个自定义协议的例子,其中包含了拆包的内容。

    所有的类:

    协议类:

    public class PersonProtocol {
      private int length;
      private byte[] content;
      public int getLength() {
        return length;
      }
      public void setLength(int length) {
        this.length = length;
      }
      public byte[] getContent() {
        return content;
      }
      public void setContent(byte[] content) {
        this.content = content;
      }
    }
    

    解码器类:

    public class PersonProtocolDecode extends ReplayingDecoder<Void> {
      @Override
      protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("decode invoke!");
      //拆包的内容如下:先获取长度,再根据获取的长度,获取到包内的内容。 int length = in.readInt(); byte[] content = new byte[length];
      //获取到内容 in.readBytes(content); PersonProtocol personProtocol = new PersonProtocol(); personProtocol.setLength(length); personProtocol.setContent(content); out.add(personProtocol); } }

    编码器类:

    public class PersonProtocolEncode extends MessageToByteEncoder<PersonProtocol>{
      @Override
      protected void encode(ChannelHandlerContext ctx, PersonProtocol msg, ByteBuf out) throws Exception {
        System.out.println("encode invoke!");
        out.writeInt(msg.getLength());
        out.writeBytes(msg.getContent());
        //不需要flush的原因是,此时还是在jvm内部处理代码,并未涉及到io
      }
    }
    

    服务器处理类:

    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import java.nio.charset.Charset;
    import java.util.UUID;
    
    public class ServerHandler extends SimpleChannelInboundHandler<PersonProtocol>{
      private int count = 0;
      @Override
      protected void channelRead0(ChannelHandlerContext ctx, PersonProtocol msg) throws Exception {
        System.out.println("接收到的消息长度:"+msg.getLength()
            +",消息内容: "+new String(msg.getContent(), Charset.forName("utf-8")));
        System.out.println("消息的次数:"+ ++count);
        PersonProtocol result = new PersonProtocol();
        UUID uuid = UUID.randomUUID();
        byte[] count = uuid.toString().getBytes(Charset.forName("utf-8"));
        System.out.println("发给客户端的数据为:长度"+count.length+",内容:"+uuid );
        result.setLength(count.length);
        result.setContent(count);
        ctx.channel().writeAndFlush(result);
      }
    }
    

    客户端处理类:

    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import java.nio.charset.Charset;
    
    public class ClientHandler extends SimpleChannelInboundHandler<PersonProtocol>{
      private int count = 0;
      @Override
      protected void channelRead0(ChannelHandlerContext ctx, PersonProtocol msg) throws Exception {
        System.out.println("接收到的消息长度"+msg.getLength()
            +",消息内容: "+new String(msg.getContent(), Charset.forName("utf-8")));
        System.out.println("消息的次数:"+ ++count);
      }
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive");
        String str = "你好吗?,我很好!,你好吗?我很好!";
        for(int i = 0;i < str.split(",").length;i++){
          String temp = str.split(",")[i];
          System.out.println(temp+",i:"+i);
          PersonProtocol pp = new PersonProtocol();
          pp.setLength(temp.getBytes(Charset.forName("utf-8")).length);
          pp.setContent(temp.getBytes(Charset.forName("utf-8")));
          ctx.writeAndFlush(pp);
        }
      }
    }
    

    服务端启动类:

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    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 NettyServer {
      public static void main(String[] args) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup(1); //分发事件循环组
        EventLoopGroup workGroup = new NioEventLoopGroup();//处理通道事件循环组
        ServerBootstrap serverBootstrap = new ServerBootstrap();//初始化服务器
        serverBootstrap.group(bossGroup,workGroup) //将两个循环组绑定到服务器
            .channel(NioServerSocketChannel.class) //指定通道类型,当前使用NIO模式
            .childHandler(new ChannelInitializer<SocketChannel>(){ //指定通道中的过滤器链
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new PersonProtocolDecode()); //解码器过滤
            pipeline.addLast(new PersonProtocolEncode());//编码器
            pipeline.addLast(new ServerHandler());//具体的业务处理,一般放在最后面
          }
        });
        ChannelFuture channelFuture = serverBootstrap.bind(12345).sync();//绑定到本机的12345端口,等待同步处理结果
        channelFuture.channel().closeFuture().sync();//阻塞等待closeFuture的返回,同步等待
        bossGroup.shutdownGracefully();//优雅关闭
        workGroup.shutdownGracefully();
      }
    }
    

    客户端启动类:

    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    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 NettyClient {
      public static void main(String[] args) throws Exception{
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).
            handler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new PersonProtocolDecode());
            pipeline.addLast(new PersonProtocolEncode());
            pipeline.addLast(new ClientHandler());
          }
        });
        ChannelFuture channelFuture = bootstrap.connect("localhost", 12345).sync();
        channelFuture.channel().closeFuture().sync();//客户端阻塞,一直运行。
        eventLoopGroup.shutdownGracefully();//优雅关闭
      }
    }
    

    运行服务端,再运行客户端,即可完成测试。

  • 相关阅读:
    JQuery插件,轻量级表单模型验证(续 二)
    关于DateTimeOffset的爱恨情仇
    JQuery插件,轻量级表单模型验证(续 一)
    asp.net 远程模型验证
    JQuery插件,轻量级表单模型验证
    Ef Core增加Sql方法
    初识依赖注入
    Core下简易WebApi
    sql语句实现行转列练习
    sql语句基本查询操作
  • 原文地址:https://www.cnblogs.com/chen--biao/p/11337581.html
Copyright © 2011-2022 走看看