最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Netty粘包拆包問題解決方案

 更新時(shí)間:2020年08月13日 08:42:13   作者:猿天地  
這篇文章主要介紹了Netty粘包拆包問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

TCP黏包拆包

TCP是一個(gè)流協(xié)議,就是沒有界限的一長串二進(jìn)制數(shù)據(jù)。TCP作為傳輸層協(xié)議并不不了解上層業(yè)務(wù)數(shù)據(jù)的具體含義,它會(huì)根據(jù)TCP緩沖區(qū)的實(shí)際情況進(jìn)行數(shù)據(jù)包的劃分,所以在業(yè)務(wù)上認(rèn)為是一個(gè)完整的包,可能會(huì)被TCP拆分成多個(gè)包進(jìn)行發(fā)送,也有可能把多個(gè)小的包封裝成一個(gè)大的數(shù)據(jù)包發(fā)送,這就是所謂的TCP粘包和拆包問題。
怎么解決?

  • • 消息定長度,傳輸?shù)臄?shù)據(jù)大小固定長度,例如每段的長度固定為100字節(jié),如果不夠空位補(bǔ)空格
  • • 在數(shù)據(jù)包尾部添加特殊分隔符,比如下劃線,中劃線等
  • • 將消息分為消息頭和消息體,消息頭中包含表示信息的總長度

Netty提供了多個(gè)解碼器,可以進(jìn)行分包的操作,分別是:

  • • LineBasedFrameDecoder (回車換行分包)
  • • DelimiterBasedFrameDecoder(特殊分隔符分包)
  • • FixedLengthFrameDecoder(固定長度報(bào)文來分包)
  • • LengthFieldBasedFrameDecoder(自定義長度來分包)

制造粘包和拆包問題

為了驗(yàn)證我們的解碼器能夠解決這種粘包和拆包帶來的問題,首先我們就制造一個(gè)這樣的問題,以此用來做對(duì)比。
服務(wù)端:

public static void main(String[] args) {
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<SocketChannel>() { 
          @Override
          public void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast("decoder", new StringDecoder());
            ch.pipeline().addLast("encoder", new StringEncoder());
            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
              @Override
              public void channelRead(ChannelHandlerContext ctx, Object msg) {
                System.err.println("server:" + msg.toString());
                ctx.writeAndFlush(msg.toString() + "你好" );
              }
            });
          }
        })
        .option(ChannelOption.SO_BACKLOG, 128)
        .childOption(ChannelOption.SO_KEEPALIVE, true);
    try {
      ChannelFuture f = bootstrap.bind(2222).sync();
       f.channel().closeFuture().sync();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }

客戶端我們發(fā)送一個(gè)比較長的字符串,如果服務(wù)端收到的消息是一條,那么就是對(duì)的,如果是多條,那么就有問題了。

public static void main(String[] args) {
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    Channel channel = null;
    try {
      Bootstrap b = new Bootstrap();
      b.group(workerGroup);
      b.channel(NioSocketChannel.class);
      b.option(ChannelOption.SO_KEEPALIVE, true);
      b.handler(new ChannelInitializer<SocketChannel>() {
        @Override
        public void initChannel(SocketChannel ch) throws Exception {
          ch.pipeline().addLast("decoder", new StringDecoder());
          ch.pipeline().addLast("encoder", new StringEncoder());
          ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) {
              System.err.println("client:" + msg.toString());
            }
          });
        }
      });
      ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
      channel = f.channel();
      StringBuilder msg = new StringBuilder();
      for (int i = 0; i < 100; i++) {
        msg.append("hello yinjihuan");
      }
      channel.writeAndFlush(msg);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }

首先啟動(dòng)服務(wù)端,然后再啟動(dòng)客戶端,通過控制臺(tái)可以看到服務(wù)接收的數(shù)據(jù)分成了2次,這就是我們要解決的問題。

server:hello yinjihuanhello....
server:o yinjihuanhello...

LineBasedFrameDecoder

用LineBasedFrameDecoder 來解決需要在發(fā)送的數(shù)據(jù)結(jié)尾加上回車換行符,這樣LineBasedFrameDecoder 才知道這段數(shù)據(jù)有沒有讀取完整。

改造服務(wù)端代碼,只需加上LineBasedFrameDecoder 解碼器即可,構(gòu)造函數(shù)的參數(shù)是數(shù)據(jù)包的最大長度。

 public void initChannel(SocketChannel ch) throws Exception {
   ch.pipeline().addLast(new LineBasedFrameDecoder(10240));
   ch.pipeline().addLast("decoder", new StringDecoder());
   ch.pipeline().addLast("encoder", new StringEncoder());
   ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.err.println("server:" + msg.toString());
        ctx.writeAndFlush(msg.toString() + "你好");
      }
   });
}

改造客戶端發(fā)送代碼,再數(shù)據(jù)后面加上回車換行符

ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
channel = f.channel();
StringBuilder msg = new StringBuilder();
for (int i = 0; i < 100; i++) {
  msg.append("hello yinjihuan");
}
channel.writeAndFlush(msg + System.getProperty("line.separator"));

DelimiterBasedFrameDecoder

DelimiterBasedFrameDecoder和LineBasedFrameDecoder差不多,DelimiterBasedFrameDecoder可以自己定義需要分割的符號(hào),比如下劃線,中劃線等等。
改造服務(wù)端代碼,只需加上DelimiterBasedFrameDecoder解碼器即可,構(gòu)造函數(shù)的參數(shù)是數(shù)據(jù)包的最大長度。我們用下劃線來分割。

public void initChannel(SocketChannel ch) throws Exception {
   ch.pipeline().addLast(new DelimiterBasedFrameDecoder(10240, Unpooled.copiedBuffer("_".getBytes())));
   ch.pipeline().addLast("decoder", new StringDecoder());
   ch.pipeline().addLast("encoder", new StringEncoder());
   ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.err.println("server:" + msg.toString());
        ctx.writeAndFlush(msg.toString() + "你好");
      }
   });
}

改造客戶端發(fā)送代碼,再數(shù)據(jù)后面加上下劃線

ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
channel = f.channel();
StringBuilder msg = new StringBuilder();
for (int i = 0; i < 100; i++) {
  msg.append("hello yinjihuan");
}
channel.writeAndFlush(msg + "_");

FixedLengthFrameDecoder

FixedLengthFrameDecoder是按固定的數(shù)據(jù)長度來進(jìn)行解碼的,也就是說你客戶端發(fā)送的每條消息的長度是固定的,下面我們看看怎么使用。

服務(wù)端還是一樣,增加FixedLengthFrameDecoder解碼器即可。

 public void initChannel(SocketChannel ch) throws Exception {
   ch.pipeline().addLast(new FixedLengthFrameDecoder(1500));
   ch.pipeline().addLast("decoder", new StringDecoder());
   ch.pipeline().addLast("encoder", new StringEncoder());
   ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.err.println("server:" + msg.toString());
        ctx.writeAndFlush(msg.toString() + "你好");
      }
   });
}

客戶端,msg輸出的長度就是1500

ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
channel = f.channel();
StringBuilder msg = new StringBuilder();
for (int i = 0; i < 100; i++) {
  msg.append("hello yinjihuan");
}
System.out.println(msg.length());
channel.writeAndFlush(msg);

服務(wù)端代碼:

public void initChannel(SocketChannel ch) throws Exception {
   ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
   ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(4));
   ch.pipeline().addLast("decoder", new StringDecoder());
   ch.pipeline().addLast("encoder", new StringEncoder());
   ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.err.println("server:" + msg.toString());
        ctx.writeAndFlush(msg.toString() + "你好");
      }
   });
}

客戶端,直接發(fā)送就行

ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
channel = f.channel();![](https://s4.51cto.com/images/blog/202008/04/fb05cdb6bd8458bd1006a127ff9d12dc.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
StringBuilder msg = new StringBuilder();
for (int i = 0; i < 100; i++) {
  msg.append("hello yinjihuan");
}
channel.writeAndFlush(msg);

源碼參考:https://github.com/yinjihuan/netty-im

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Security+Spring Data Jpa如何進(jìn)行安全管理

    Spring Security+Spring Data Jpa如何進(jìn)行安全管理

    這篇文章主要介紹了Spring Security+Spring Data Jpa如何進(jìn)行安全管理,幫助大家更好的理解和學(xué)習(xí)Spring Security框架,感興趣的朋友可以了解下
    2020-09-09
  • JAVA構(gòu)造器是否為靜態(tài)方法你知道嗎

    JAVA構(gòu)造器是否為靜態(tài)方法你知道嗎

    這篇文章主要為大家詳細(xì)介紹了JAVA構(gòu)造器是否為靜態(tài)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • Java數(shù)據(jù)結(jié)構(gòu)之散列表詳解

    Java數(shù)據(jù)結(jié)構(gòu)之散列表詳解

    散列表(Hash table,也叫哈希表),是根據(jù)關(guān)鍵碼值(Key value)而直接進(jìn)行訪問的數(shù)據(jù)結(jié)構(gòu)。本文將為大家具體介紹一下散列表的原理及其代碼實(shí)現(xiàn)
    2022-01-01
  • 一個(gè)簡陋的java圖書管理系統(tǒng)

    一個(gè)簡陋的java圖書管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了一個(gè)簡陋的java圖書管理系統(tǒng),簡單的實(shí)現(xiàn)功能測試,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Spring?Cache簡單介紹和使用大全

    Spring?Cache簡單介紹和使用大全

    Spring?Cache是一個(gè)框架,實(shí)現(xiàn)了基于注解的緩存功能,只需要簡單地加一個(gè)注解,就能實(shí)現(xiàn)緩存功能,這篇文章主要介紹了Spring?Cache簡介和使用,需要的朋友可以參考下
    2023-03-03
  • Spring?Get請(qǐng)求與post請(qǐng)求的實(shí)現(xiàn)

    Spring?Get請(qǐng)求與post請(qǐng)求的實(shí)現(xiàn)

    在Spring中,GET請(qǐng)求和POST請(qǐng)求是兩種常見的HTTP請(qǐng)求方法,用于與服務(wù)器進(jìn)行交互,本文詳細(xì)的介紹一下Spring?Get請(qǐng)求與post請(qǐng)求的實(shí)現(xiàn),感興趣的可以了解一下
    2023-10-10
  • Java編程實(shí)現(xiàn)鄰接矩陣表示稠密圖代碼示例

    Java編程實(shí)現(xiàn)鄰接矩陣表示稠密圖代碼示例

    這篇文章主要介紹了Java編程實(shí)現(xiàn)鄰接矩陣表示稠密圖代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Java中左移和右移問題圖文詳解

    Java中左移和右移問題圖文詳解

    左移和右移并不常用,在一些特殊情況下才會(huì)使用,比如加解密時(shí),會(huì)大量用到,這篇文章主要給大家介紹了關(guān)于Java中左移和右移問題的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • Java 實(shí)現(xiàn)對(duì)稱加密算法

    Java 實(shí)現(xiàn)對(duì)稱加密算法

    這篇文章主要介紹了Java 實(shí)現(xiàn)對(duì)稱加密算法的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-11-11
  • SpringBoot攔截器使用精講

    SpringBoot攔截器使用精講

    攔截器可以根據(jù) URL 對(duì)請(qǐng)求進(jìn)行攔截,主要應(yīng)用于登陸校驗(yàn)、權(quán)限驗(yàn)證、亂碼解決、性能監(jiān)控和異常處理等功能上。SpringBoot同樣提供了攔截器功能。 本文將為大家詳細(xì)介紹一下
    2021-12-12

最新評(píng)論

赤水市| 龙南县| 平和县| 海口市| 神农架林区| 阿拉尔市| 舒兰市| 竹山县| 凌源市| 安多县| 临澧县| 长阳| 金沙县| 遵化市| 桑日县| 关岭| 武城县| 甘南县| 泌阳县| 纳雍县| 宜兰市| 阿拉善左旗| 应城市| 张家界市| 尼木县| 青神县| 前郭尔| 无极县| 兴国县| 翁源县| 夏邑县| 高陵县| 惠州市| 铜山县| 玛纳斯县| 阿勒泰市| 仁布县| 厦门市| 土默特右旗| 崇义县| 连山|