1. 程式人生 > >Netty 之 TCP粘包拆包基本解決方案

Netty 之 TCP粘包拆包基本解決方案

上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案

Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述的粘包和拆包的問題,歸根結底的解決方案就是傳送端給遠端端一個標記,告訴遠端端,每個資訊的結束標誌是什麼,這樣,遠端端獲取到資料後,根據跟傳送端約束的標誌,將接收的資訊分切或者合併成我們需要的資訊,這樣我們就可以獲取到正確的資訊了

例如,我們剛才的例子中,我們可以在傳送的資訊中,加一個結束標誌,例如兩個遠端端規定以行來切分資料,那麼傳送端,就需要在每個資訊體的末尾加上行結束的標誌,部分程式碼如下:

修改BaseClientHandler的req的構造:

  1. public BaseClientHandler() {  
  2. //        req = ("BazingaLyncc is learner").getBytes();
  3.         req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. His book w"
  4.                 + "ill give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the process "
  5.                 + "of configuring and connecting all of Netty’s components to bring your learned about threading models in ge"
  6.                 + "neral and Netty’s threading model in particular, whose performance and consistency advantages we discuss"
  7.                 + "ed in detail In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. Hi"
  8.                 + "s book will give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the"
  9.                 + " process of configuring and connecting all of Netty’s components to bring your learned about threading "
  10.                 + "models in general and Netty’s threading model in particular, whose performance and consistency advantag"
  11.                 + "es we discussed in detailIn this chapter you general, we recommend Java Concurrency in Practice by Bri"
  12.                 + "an Goetz. His book will give We’ve reached an exciting point—in the next chapter;the counter is: 1 2222"
  13.                 + "sdsa ddasd asdsadas dsadasdas" + System.getProperty("line.separator")).getBytes();  
  14.     }  
我們在我們巨長的req中末尾加了System.getProperty("line.separator"),這樣相當於給req打了一個標記

打完標記,其實我們這個示例中的server中還不知道是以行為結尾的,所以我們需要修改server的handler鏈,在inbound鏈中加一個額外的處理鏈,判斷一下,獲取的資訊按照行來切分,我們很慶幸,這樣枯燥的程式碼Netty已經幫我們完美地完成了,Netty提供了一個LineBasedFrameDecoder這個類,顧名思義,這個類名字中有decoder,說明是一個解碼器,我們再看看它的詳細宣告:

  1. /** 
  2.  * A decoder that splits the received {@link ByteBuf}s on line endings. 
  3.  * <p> 
  4.  * Both {@code "\n"} and {@code "\r\n"} are handled. 
  5.  * For a more general delimiter-based decoder, see {@link DelimiterBasedFrameDecoder}. 
  6.  */
  7. publicclass LineBasedFrameDecoder extends ByteToMessageDecoder {  
  8.     /** Maximum length of a frame we're willing to decode.  */
  9.     privatefinalint maxLength;  
  10.     /** Whether or not to throw an exception as soon as we exceed maxLength. */
  11.     privatefinalboolean failFast;  
  12.     privatefinalboolean stripDelimiter;  
  13.     /** True if we're discarding input because we're already over maxLength.  */
  14.     privateboolean discarding;  
  15.     privateint discardedBytes;  
它是繼承ByteToMessageDecoder的,是將byte型別轉化成Message的,所以我們應該將這個解碼器放在inbound處理器鏈的第一個,所以我們修改一下Server端的啟動程式碼:
  1. package com.lyncc.netty.stickpackage.myself;  
  2. import io.netty.bootstrap.ServerBootstrap;  
  3. import io.netty.channel.ChannelFuture;  
  4. import io.netty.channel.ChannelInitializer;  
  5. import io.netty.channel.ChannelOption;  
  6. import io.netty.channel.EventLoopGroup;  
  7. import io.netty.channel.nio.NioEventLoopGroup;  
  8. import io.netty.channel.socket.SocketChannel;  
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;  
  10. import io.netty.handler.codec.LineBasedFrameDecoder;  
  11. import io.netty.handler.codec.string.StringDecoder;  
  12. import java.net.InetSocketAddress;  
  13. publicclass BaseServer {  
  14.     privateint port;  
  15.     public BaseServer(int port) {  
  16.         this.port = port;  
  17.     }  
  18.     publicvoid start(){  
  19.         EventLoopGroup bossGroup = new NioEventLoopGroup(1);  
  20.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
  21.         try {  
  22.             ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))  
  23.                     .childHandler(new ChannelInitializer<SocketChannel>() {  
  24.                         protectedvoid initChannel(SocketChannel ch) throws Exception {  
  25.                             ch.pipeline().addLast(new LineBasedFrameDecoder(2048));  
  26.                             ch.pipeline().addLast(new StringDecoder());  
  27.                             ch.pipeline().addLast(new BaseServerHandler());  
  28.                         };  
  29.                     }).option(ChannelOption.SO_BACKLOG, 128)     
  30.                     .childOption(ChannelOption.SO_KEEPALIVE, true);  
  31.              // 繫結埠,開始接收進來的連線
  32.              ChannelFuture future = sbs.bind(port).sync();    
  33.              System.out.println("Server start listen at " + port );  
  34.              future.channel().closeFuture().sync();  
  35.         } catch (Exception e) {  
  36.             bossGroup.shutdownGracefully();  
  37.             workerGroup.shutdownGracefully();  
  38.         }  
  39.     }  
  40.     publicstaticvoid main(String[] args) throws Exception {  
  41.         int port;  
  42.         if (args.length > 0) {  
  43.             port = Integer.parseInt(args[0]);  
  44.         } else {  
  45.             port = 8080;  
  46.         }  
  47.         new BaseServer(port).start();  
  48.     }  
  49. }  
這樣,我們只是在initChannel方法中增加了一個LineBasedFrameDecoder這個類,其中2048是規定一行資料最大的位元組數

我們再次執行,我們再看看效果:

可以看到客戶端傳送的兩次msg,被伺服器端成功地兩次接收了,我們要的效果達到了

我們將LineBasedFrameDecoder中的2048引數,縮小一半,變成1024,我們再看看效果:

出現了異常,這個異常時TooLongFrameException,這個異常在Netty in Action中介紹過,幀的大小太大,在我們這個場景中,就是我們傳送的一行資訊大小是1076,大於了我們規定的1024所以報錯了

我們再解決另一個粘包的問題,我們可以看到上節中介紹的那個粘包案例中,我們傳送了100次的資訊“BazingaLyncc is learner”,這個案例很特殊,這個資訊是一個特長的資料,位元組長度是23,所以我們可以使用Netty為我們提供的FixedLengthFrameDecoder這個解碼器,看到這個名字就明白了大半,定長資料幀的解碼器,所以我們修改一下程式碼:

BaseClientHandler:

  1. package com.lyncc.netty.stickpackage.myself;  
  2. import io.netty.buffer.ByteBuf;  
  3. import io.netty.buffer.Unpooled;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.channel.ChannelInboundHandlerAdapter;  
  6. publicclass BaseClientHandler extends ChannelInboundHandlerAdapter{  
  7.     privatebyte[] req;  
  8.     public BaseClientHandler() {  
  9.         req = ("BazingaLyncc is learner").getBytes();  
  10. //        req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. His book w"
  11. //                + "ill give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the process "
  12. 相關推薦

    Netty TCP基本解決方案

    上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案 Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述的粘包和拆包的問題,歸根結底的解決方案就是傳送端給遠端端一個標記,告訴遠端端,

    一起學Netty(七) TCP基本解決方案

    上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案 Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述的粘包和拆包的問題,歸根結底的解決方案就是傳送端給遠端端一個標記,告訴遠端端,每個資訊的結束

    netty權威指南學習筆記四——TCP/問題解決

    方法 pan 對象 protect row 學習 ddl .get font   發生了粘包,我們需要將其清晰的進行拆包處理,這裏采用LineBasedFrameDecoder來解決 LineBasedFrameDecoder的工作原理是它依次遍歷ByteBuf中的可讀字節

    Netty學習路(五)-TCP/問題

    TCP是個“流協議”,所謂流,就是沒有界限的一串資料。TCP底層並不瞭解上層業務資料的具體含義,它會根據TCP緩衝區的實際情況進行包的劃分,所以一個完整的包可能會被TCP拆分成多個包進行傳送,也有可能吧多個小的包封裝成一個大的資料包傳送,這就是TCP粘包和拆包問題。 TCP粘包/拆包產生

    【轉】Netty解決TCP(自定義協議)

    https://www.cnblogs.com/sidesky/p/6913109.html 1、什麼是粘包/拆包        一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方

    Netty解決TCP(設定訊息邊界)

    1、什麼是粘包/拆包        一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,

    Netty精粹TCP問題

    粘包拆包問題是處於網路比較底層的問題,在資料鏈路層、網路層以及傳輸層都有可能發生。我們日常的網路應用開發大都在傳輸層進行,由於UDP有訊息保護邊界,不會發生這個問題,因此這篇文章只討論發生在傳輸層的TCP粘包拆包問題。 什麼是粘包、拆包? 對於什麼是粘包、拆包問題,

    Netty解決TCP(自定義協議)

    1、什麼是粘包/拆包        一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,導

    一起學Netty(六) TCP場景

    TCP程式設計底層都有粘包和拆包機制,因為我們在C/S這種傳輸模型下,以TCP協議傳輸的時候,在網路中的byte其實就像是河水,TCP就像一個搬運工,將這流水從一端轉送到另一端,這時又分兩種情況: 1)如果客戶端的每次製造的水比較多,也就是我們常說的客戶端給的包比較大,TC

    TCP基本解決方案

    scu fonts println mar 是我 perf throws 自己 切割 上個小節我們淺析了在Netty的使用的時候TCP的粘包和拆包的現象,Netty對此問題提供了相對比較豐富的解決方案 Netty提供了幾個常用的解碼器,幫助我們解決這些問題,其實上述

    netty學習筆記一:TCP

    min -s 原因 兩個 image 分享 技術 ima 選項 什麽是TCP拆包粘包 假設客戶端發送了2條消息M1,M2。可能會出現以下幾種情況。 1、服務端正常接收到M1,M2這兩條消息。 2、服務端一次接收到了2個數據包,M1和M2粘合在一起,這時候就被稱為TCP粘包

    【Netty4.x】Netty TCP/問題的解決辦法(二)

    一、什麼是TCP粘包/拆包  如圖所示,假如客戶端分別傳送兩個資料包D1和D2給服務端,由於服務端一次讀取到的位元組數是不確定的,故可能存在以下4中情況:第一種情況:Server端分別讀取到D1和D2,沒有產生粘包和拆包的情況。第二種情況:Server端一次接收到兩個資料包,

    java架構路-(netty專題)netty的編解碼(出入戰)與

    上次迴歸:   上次部落格我們主要說了netty的基本使用,都是一些固定的模式去寫的,我們只需要關注我們的攔截器怎麼去寫就可以了,然後我們用我們的基礎示例,改造了一個簡單的聊天室程式,可以看到內部加了一個StringEncoder和StringDecoder,這個就是用來編解碼我們字串的,這次我們就來說說這個

    NettyTCP問題代碼示例與分析

    sep 會有 value 指南 esp syn logger soc pipe [toc] Netty中TCP粘包問題代碼示例 Netty中會發生TCP粘包和拆包的問題,當然,其實對於曾經的網絡工程師來說,第一次看到這名詞可能會有點不適應,因為在那會我們是說TCP的累計發

    netty的解碼器和

    exception med 增加 這就是 就會 邊界 ali 空格 封裝 Tcp是一個流的協議,一個完整的包可能會被Tcp拆成多個包進行發送,也可能把一個小的包封裝成一個大的數據包發送,這就是所謂的粘包和拆包問題 粘包、拆包出現的原因: 在流傳輸中出現,UDP不會出現粘包,

    Netty | 史上最全解讀

    Netty 粘包/半包原理與拆包實戰(史上最全) 瘋狂創客圈 Java 聊天程式【 億級流量】實戰系列之13 【部落格園 總入口 】 本文的原始碼工程:Netty 粘包/半包原理與拆包實戰 原始碼 本例項是《Netty 粘包/半包原理與拆包實戰》 一文的原始碼工程

    Netty權威指南_札記04_TCP/問題解決

    文章目錄 Netty權威指南_札記04_TCP粘包/拆包問題解決 1. TCP粘包/拆包 1.1 TCP粘包/拆包問題說明 1.2 TCP粘包/拆包發生的原因 1.3 粘包問題解決策略

    Unity C# 自定義TCP傳輸協議以及封解決問題(網路應用層協議)

    本文只是初步實現了一個簡單的基於TCP的自定協議,更為複雜的協議可以根據這種方式去擴充套件。 網路應用層協議,通俗一點的講,它是一種基於socket傳輸的由傳送方和接收方事先協商好的一種訊息包組成結構,主要由訊息頭和訊息體組成。  眾所周知,基於socket的資訊互動有兩

    python網路程式設計——tcp&udp丟

    一、tcp粘包問題產生的原因: 傳送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle演算法),將多次間隔較小且資料量小的資料,合併成一個大的資料塊,然後進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 二、兩種情況下會發

    Socket TCP

    TCP(transport control protocol,傳輸控制協議)是面向連線的,面向流的,提供高可靠性服務。收發兩端(客戶端和伺服器端)都要有一一成對的socket,因此,傳送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle演算法),將多次