一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Netty粘包拆包問題解決方案

Netty粘包拆包問題解決方案

2020-08-13 16:33猿天地 Java教程

這篇文章主要介紹了Netty粘包拆包問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

TCP黏包拆包

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

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

Netty提供了多個解碼器,可以進行分包的操作,分別是:

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

制造粘包和拆包問題

為了驗證我們的解碼器能夠解決這種粘包和拆包帶來的問題,首先我們就制造一個這樣的問題,以此用來做對比。
服務端:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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();
    }
  }

客戶端我們發送一個比較長的字符串,如果服務端收到的消息是一條,那么就是對的,如果是多條,那么就有問題了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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();
    }
  }

首先啟動服務端,然后再啟動客戶端,通過控制臺可以看到服務接收的數據分成了2次,這就是我們要解決的問題。

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

LineBasedFrameDecoder

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

改造服務端代碼,只需加上LineBasedFrameDecoder 解碼器即可,構造函數的參數是數據包的最大長度。

?
1
2
3
4
5
6
7
8
9
10
11
12
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() + "你好");
      }
   });
}

改造客戶端發送代碼,再數據后面加上回車換行符

?
1
2
3
4
5
6
7
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可以自己定義需要分割的符號,比如下劃線,中劃線等等。
改造服務端代碼,只需加上DelimiterBasedFrameDecoder解碼器即可,構造函數的參數是數據包的最大長度。我們用下劃線來分割。

?
1
2
3
4
5
6
7
8
9
10
11
12
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() + "你好");
      }
   });
}

改造客戶端發送代碼,再數據后面加上下劃線

?
1
2
3
4
5
6
7
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是按固定的數據長度來進行解碼的,也就是說你客戶端發送的每條消息的長度是固定的,下面我們看看怎么使用。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
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

?
1
2
3
4
5
6
7
8
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);

服務端代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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() + "你好");
      }
   });
}

客戶端,直接發送就行

?
1
2
3
4
5
6
7
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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://blog.51cto.com/14888386/2516865

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美a在线| 精品AV无码一二三区视频 | 忘忧草研究院一二三 | 欧美性理论片在线观看片免费 | 亚洲精品色图 | 深夜福利影院在线观看 | 欧美亚洲国产精品久久第一页 | aⅴ天堂小视频 | 免费一级欧美片在线观看 | 国产精品刺激好大好爽视频 | 欧美男同videos | 爱情岛论坛亚洲自拍 | 日本乱中文字幕系列在线观看 | 免费叼嘿视频 | 国产人成激情视频在线观看 | 韩国www| 91精品免费国产高清在线 | 香蕉国产成版人视频在线观看 | 国内精品久久久久久中文字幕 | 国外成品精品1688 | 美女黄a | 精品日韩欧美一区二区三区在线播放 | 91普通话国产对白在线 | 免看一级一片一在线看 | 波多野结衣178部中文字幕 | 国产精品露脸国语对白手机视频 | 国产成人精品本亚洲 | 欧美人体高清在线观看ggogo | 亚洲AV国产精品无码精 | 公妇仑乱在线观看 | 国产欧美视频在线观看 | 亚洲欧美乱 | 国产成人精品在线观看 | 91yellow吧字幕网zmff7 | 亚洲免费大全 | 四虎影院的网址 | 国产精品资源站 | 久青草国产观看在线视频 | 国产99视频精品免费视频免里 | 精品国产综合 | 桥本有菜在线四虎福利网 |