netty如何解決TCP粘包問題,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
因為自己造一個RPC框架的輪子時,需要解決TCP的粘包問題,特此記錄,希望方便他人。這是我寫的RPC框架的 GitHub地址 https://github.com/yangzhenkun/krpc。
歡迎star,fork。已經寫了多篇文章對這個框架的原理進行說明。對原理有興趣的歡迎交流。
TCP粘包就是在TCP數據傳輸過程中,因為某些原因,接收方收到讀取的數據并不是但存的一次數據,而是多個數據包的字節流組裝在一起,導致多個數據粘在一起,接收端在讀取的時候不知道怎么樣把數據分成預期的多組數據,這就是粘包。
TCP之所以造成粘包現象是因為其發送端和接收端的緩沖區及TCP數據流引起的。
例如nagle算法,會將瞬間的很多小包數據拼裝稱一個大的數據,以提高的帶寬的利用率。(具體nagle算法就不展開將)。
但即使關閉了nagle算法,粘包依舊存在。因為這不是造成tcp粘包的根本原因。因為有緩沖區的存在,在緩存區沒有打滿之前是不會發送出去的,同時接收端也是利用緩存區接收數據,在接著從緩存區讀取接收的數據解析。這時有人問,如果數據量很小,總是沒有打滿緩沖區那怎么辦,這就依賴發送和接收端的定時器了,他們會定時的處理數據,要不這不就成了bug了。
就是因為緩沖區的存在以及tcp數據流的形式,造成了多組數據的拼接,形成了粘包,半 包問題。
目前常用的方法是定義 起始 邊屆符+數據長度來告訴接收端一個數據包具體的長度。
不過也有定義固定長度的,不過這樣可能會造成的空白字節的浪費以及超出長度這種不易擴展的方式。純邊界符的方式 怕發生實際消息體與邊界符的碰撞,造成消息的誤截斷。
netty對NIO模式的TCP通信的封裝可謂是完美??勺屓丝焖賹懗隹捎玫膖cp通信的服務端和客戶端,并且很簡單的解決粘包問題。
netty有提供基于分隔符和長度的編解碼器,方便開發者使用。
DelimiterBaseFrameDecoder是可以用戶自定義數據分隔符來分割的,LineBaseFrameDecoder是由行尾符(\n或者\r\n)分割,速度比前者還要塊。
還有基于長度的FixedLengthFrameDecoder定長的解碼器,LengthFieldBasedFrameDecoder動態長度的解碼器。這4中方式都有對應的編解碼器。
同時對于數據類型的邊界嗎,netty也支持byte,string,protobuf等,大家可以去看MessageToMessageDecoder的子類,就能發現netty提供很多編解碼的規則。
在自己寫KRPC時,一開始沒有把NIO的計劃提這么早,奈何在第一版用同步IO寫客戶端,壓測時發現竟然那么不堪,遂決定用NIO改寫,一開始覺得用Netty寫客戶端不方便(當時沒到怎么寫),便決定用java原生的NIO來寫客戶端,寫到最后發現處理粘包特別困難,需要自己定義 特殊分界符號,然后設置長度,在客戶端和服務端解析起來特別繁雜。于是嘗試用netty寫,發現特別簡單。
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast("decoder", new ByteArrayDecoder());
pipeline.addLast("encoder", new ByteArrayEncoder());
pipeline.addLast(new ServerHandler());
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64,Global.getInstance().getMaxBuf(),Global.getInstance().getMaxBuf()));
這就是服務端的代碼,有沒有特別簡單,因為TCP將傳輸的數據序列化由壓縮后的數據為 字節數組,所以使用的自帶的ByteArray編解碼器,使用了動態長度的LengthFieldBaseFrame來解決粘包問題。就這樣解決了粘包問題。
關于netty如何解決TCP粘包問題問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。