溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

netty如何解決TCP粘包問(wèn)題

發(fā)布時(shí)間:2021-12-06 11:10:27 來(lái)源:億速云 閱讀:414 作者:柒染 欄目:大數(shù)據(jù)

netty如何解決TCP粘包問(wèn)題,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

0.起因

因?yàn)樽约涸煲粋€(gè)RPC框架的輪子時(shí),需要解決TCP的粘包問(wèn)題,特此記錄,希望方便他人。這是我寫(xiě)的RPC框架的 GitHub地址 https://github.com/yangzhenkun/krpc。 
歡迎star,fork。已經(jīng)寫(xiě)了多篇文章對(duì)這個(gè)框架的原理進(jìn)行說(shuō)明。對(duì)原理有興趣的歡迎交流。

1.什么是粘包

1.1 什么是TCP粘包

TCP粘包就是在TCP數(shù)據(jù)傳輸過(guò)程中,因?yàn)槟承┰颍邮辗绞盏阶x取的數(shù)據(jù)并不是但存的一次數(shù)據(jù),而是多個(gè)數(shù)據(jù)包的字節(jié)流組裝在一起,導(dǎo)致多個(gè)數(shù)據(jù)粘在一起,接收端在讀取的時(shí)候不知道怎么樣把數(shù)據(jù)分成預(yù)期的多組數(shù)據(jù),這就是粘包。

1.2 形成原因

TCP之所以造成粘包現(xiàn)象是因?yàn)槠浒l(fā)送端和接收端的緩沖區(qū)及TCP數(shù)據(jù)流引起的。

例如nagle算法,會(huì)將瞬間的很多小包數(shù)據(jù)拼裝稱一個(gè)大的數(shù)據(jù),以提高的帶寬的利用率。(具體nagle算法就不展開(kāi)將)。

但即使關(guān)閉了nagle算法,粘包依舊存在。因?yàn)檫@不是造成tcp粘包的根本原因。因?yàn)橛芯彌_區(qū)的存在,在緩存區(qū)沒(méi)有打滿之前是不會(huì)發(fā)送出去的,同時(shí)接收端也是利用緩存區(qū)接收數(shù)據(jù),在接著從緩存區(qū)讀取接收的數(shù)據(jù)解析。這時(shí)有人問(wèn),如果數(shù)據(jù)量很小,總是沒(méi)有打滿緩沖區(qū)那怎么辦,這就依賴發(fā)送和接收端的定時(shí)器了,他們會(huì)定時(shí)的處理數(shù)據(jù),要不這不就成了bug了。

就是因?yàn)榫彌_區(qū)的存在以及tcp數(shù)據(jù)流的形式,造成了多組數(shù)據(jù)的拼接,形成了粘包,半 包問(wèn)題。

1.3 如何解決

目前常用的方法是定義 起始 邊屆符+數(shù)據(jù)長(zhǎng)度來(lái)告訴接收端一個(gè)數(shù)據(jù)包具體的長(zhǎng)度。

不過(guò)也有定義固定長(zhǎng)度的,不過(guò)這樣可能會(huì)造成的空白字節(jié)的浪費(fèi)以及超出長(zhǎng)度這種不易擴(kuò)展的方式。純邊界符的方式 怕發(fā)生實(shí)際消息體與邊界符的碰撞,造成消息的誤截?cái)唷?/p>

2.netty如何解決

netty對(duì)NIO模式的TCP通信的封裝可謂是完美??勺屓丝焖賹?xiě)出可用的tcp通信的服務(wù)端和客戶端,并且很簡(jiǎn)單的解決粘包問(wèn)題。

netty有提供基于分隔符和長(zhǎng)度的編解碼器,方便開(kāi)發(fā)者使用。 
DelimiterBaseFrameDecoder是可以用戶自定義數(shù)據(jù)分隔符來(lái)分割的,LineBaseFrameDecoder是由行尾符(\n或者\(yùn)r\n)分割,速度比前者還要塊。 
還有基于長(zhǎng)度的FixedLengthFrameDecoder定長(zhǎng)的解碼器,LengthFieldBasedFrameDecoder動(dòng)態(tài)長(zhǎng)度的解碼器。這4中方式都有對(duì)應(yīng)的編解碼器。

同時(shí)對(duì)于數(shù)據(jù)類(lèi)型的邊界嗎,netty也支持byte,string,protobuf等,大家可以去看MessageToMessageDecoder的子類(lèi),就能發(fā)現(xiàn)netty提供很多編解碼的規(guī)則。

3.實(shí)戰(zhàn)-RPC框架的客戶和服務(wù)端實(shí)現(xiàn)

在自己寫(xiě)KRPC時(shí),一開(kāi)始沒(méi)有把NIO的計(jì)劃提這么早,奈何在第一版用同步IO寫(xiě)客戶端,壓測(cè)時(shí)發(fā)現(xiàn)竟然那么不堪,遂決定用NIO改寫(xiě),一開(kāi)始覺(jué)得用Netty寫(xiě)客戶端不方便(當(dāng)時(shí)沒(méi)到怎么寫(xiě)),便決定用java原生的NIO來(lái)寫(xiě)客戶端,寫(xiě)到最后發(fā)現(xiàn)處理粘包特別困難,需要自己定義 特殊分界符號(hào),然后設(shè)置長(zhǎng)度,在客戶端和服務(wù)端解析起來(lái)特別繁雜。于是嘗試用netty寫(xiě),發(fā)現(xiàn)特別簡(jiǎn)單。

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()));

這就是服務(wù)端的代碼,有沒(méi)有特別簡(jiǎn)單,因?yàn)門(mén)CP將傳輸?shù)臄?shù)據(jù)序列化由壓縮后的數(shù)據(jù)為 字節(jié)數(shù)組,所以使用的自帶的ByteArray編解碼器,使用了動(dòng)態(tài)長(zhǎng)度的LengthFieldBaseFrame來(lái)解決粘包問(wèn)題。就這樣解決了粘包問(wèn)題。

關(guān)于netty如何解決TCP粘包問(wèn)題問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI