使用Netty解決TCP粘包和拆包問題過程詳解_第1頁
使用Netty解決TCP粘包和拆包問題過程詳解_第2頁
使用Netty解決TCP粘包和拆包問題過程詳解_第3頁
使用Netty解決TCP粘包和拆包問題過程詳解_第4頁
全文預(yù)覽已結(jié)束

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、使用Netty軍決TCP粘包和拆包問題過程詳解刖百我們介紹了如果使用Netty來開發(fā)一個簡單的服務(wù)端和客戶端,接下來我們來討論如何使用解碼器來解決TCP的粘包和拆包問題TCP為什么會粘包拆包我們知道,TCP是以一種流的方式來進(jìn)行網(wǎng)絡(luò)轉(zhuǎn)播的,當(dāng)t三次握手簡歷通信后,客戶端服務(wù)端之間就建立了一種通訊管道,我們可以想象成自來水管道,流出來的水是連城一片的,是沒有分界線的。TCP底層并不了解上層的業(yè)務(wù)數(shù)據(jù)的具體含義,它會根據(jù)TCP緩沖區(qū)的實際情況進(jìn)行包的劃分。所以對于我們應(yīng)用層而言。我們直觀是發(fā)送一個個連續(xù)完整TCP數(shù)據(jù)包的,而在底層就可能會出現(xiàn)將一個完整的TCP拆分成多個包發(fā)送或者將多個包封裝成一個

2、大的數(shù)據(jù)包發(fā)送。這就是所謂的TCP粘包和拆包。當(dāng)發(fā)生TCP粘包拆包會發(fā)生什么情況我們舉一個簡單例子說明:客戶端向服務(wù)端發(fā)送兩個數(shù)據(jù)包:第一個內(nèi)容為;第二個內(nèi)容為。服務(wù)端接受一個數(shù)據(jù)并做相應(yīng)的業(yè)務(wù)處理(這里就是打印接受數(shù)據(jù)加一個逗號)。那么服務(wù)端輸出結(jié)果將會出現(xiàn)下面四種情況服務(wù)端響應(yīng)結(jié)果結(jié)論12,345,6正常接收,沒有發(fā)生粘包和拆包4異常接收,發(fā)生t粘包24,異常接收,發(fā)生t拆包25異常接收,發(fā)生t拆包和粘包如何解決主流的協(xié)議解決方案可以歸納如下:消息定長,例如每個報文的大小固定為個字節(jié),如果不夠,空位補空格;在包尾增加回車換行符進(jìn)行切割;將消息分為消息頭和消息體,消息頭中包含表示消息總長度的

3、字段;更復(fù)雜的應(yīng)用層協(xié)議。對于之刖描述的案例,在這里我們就可以采取方案1和方案3。以方案為例:我們每次發(fā)送的TCP包只有三個數(shù)字,那么我將報文設(shè)置為個字節(jié)大小的,此時,服務(wù)器就會以三個字節(jié)為基準(zhǔn)來接受包,以此來解決站包拆包問題。Netty的解決之道LineBasedFrameDecoder廢話不多說直接上代碼服務(wù)端Pteettt配置服務(wù)端的N線程組et三=newNioetworkerGroeNtyeetteServerBootsteeNServerSocketChannel.cltCetSO_BACKLOG,.childHandler(newChildChannelHandler();/綁定端

4、口,同步等待成功ChannelFuturef=b.bind(port).sync();/等待服務(wù)端監(jiān)聽端口關(guān)閉f.channel().closeFuture().sync();finally/優(yōu)雅退出,釋放線程池資源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();privateclassChildChannelHandlerextendsChannelInitializerOverrideprotectedvoidinitChannel(SocketChannelarg0)throwsExceptionarg0.p

5、ipeline().addLast(newLineBasedFrameDecoder(1024);/1arg0.pipeline().addLast(newStringDecoder();/2arg0.pipeline().addLast(newPrintServerHandler();publicstaticvoidmain(Stringargs)throwsExceptionintport=8080;newTimeServer().bind(port);服務(wù)端HandlerpublicclassPrintServerHandlerextendsChannelHandlerAdapterOv

6、erridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsExceptionByteBufbuf=(ByteBuf)msg;bytereq=newbytebuf.readableBytes();buf.readBytes(req);將緩存區(qū)的字節(jié)數(shù)組復(fù)制到新建的req數(shù)組中Stringbody=newString(req,UTF-8);System.out.println(body);Stringresponse=打印成功;ByteBufresp=Unpooled.copiedBuffer(response.get

7、Bytes();ctx.write(resp);OverridepublicvoidchannelReadComplete(ChannelHandlerContextctx)throwsExceptionctx.flush();OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)ctx.close();客戶端publicclassPrintClientpublicvoidconnect(intport,Stringhost)throwsExceptionEventLoopGroupgroup=new

8、NioEventLoopGroup();tryBootstrapb=newBootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY,true).handler(newChannelInitializer()OverridepublicvoidinitChannel(SocketChannelch)throwsExceptionch.pipeline().addLast(newLineBasedFrameDecoder(1024);/3ch.pipeline().addL

9、ast(newStringDecoder();/4ch.pipeline().addLast(newPrintClientHandler(););ChannelFuturef=b.connect(host,port).sync();f.channel().closeFuture().sync();finally/優(yōu)雅退出,釋放NIO線程組group.shutdownGracefully();/*paramargs*throwsException*/publicstaticvoidmain(Stringargs)throwsExceptionintport=8080;newTimeClient(

10、).connect(port,);客戶端的HandlerpublicclassPrintClientHandlerextendsChannelHandlerAdapterprivatestaticfinalLoggerlogger=Logger.getLogger(TimeClientHandler.class.getName();privatefinalByteBuffirstMessage;/*Createsaclient-sidehandler.*/publicTimeClientHandler()bytereq=你好服務(wù)端.getBytes();firstMessage=Unpoole

11、d.buffer(req.length);firstMessage.writeBytes(req);OverridepublicvoidchannelActive(ChannelHandlerContextctx)ctx.writeAndFlush(firstMessage);OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsExceptionByteBufbuf=(ByteBuf)msg;bytereq=newbytebuf.readableBytes();buf.readBytes(req);Str

12、ingbody=newString(req,UTF-8);System.out.println(”服務(wù)端回應(yīng)消息:+body);OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause)/釋放資源System.out.println(Unexpectedexceptionfromdownstream:+cause.getMessage();ctx.close();上訴代碼邏輯與上一章代碼邏輯相同,客戶端接受服務(wù)端數(shù)據(jù)答應(yīng),并回復(fù)客戶端信息,客戶端接受到數(shù)據(jù)后打印數(shù)據(jù)。我們觀察代碼可以發(fā)現(xiàn),要想Netty解

13、決粘包拆包問題,只需在編寫服務(wù)端和客戶端的pipeline上加上相應(yīng)的解碼器即可,上訴注釋1,2,3,4處。其余代碼無需做任何修改。LineBasedFrameDecoder+StringDecoder的組合就是按行切換的文本解碼器,它被設(shè)計用來支持TCP的粘包和拆包。原理為:如果連續(xù)讀取到最大長度后任然沒有發(fā)現(xiàn)換行符,就會拋出異常,同時忽略掉之前督導(dǎo)的異常碼流。DelimiteBasedFrameDecoder該解碼器的可以自動完成以分割符作為碼流結(jié)束標(biāo)識的消息解碼。(其實上一個解碼器類似,如果指定分隔符為換行符,那么與上一個編碼器的作用基本相同)使用也很簡單:只需要修改服務(wù)端和客戶端對應(yīng)代

14、碼中的nitChanneI代碼即可publicvoidinitChannel(SocketChannelch)ByteBufdeIimiter=UnpooIed.copiedBuffer(_.getBytes();/1ch.pipeline().addLast(newDelimiterBasedFrameDecoder(1024,delimiter);/2ch.pipeline().addLast(newStringDecoder();/3ch.pipeline().addLast(newPrintHandler();注釋1:首先創(chuàng)建分隔符緩沖對象ByteBuf,并指定以作為分隔符。注釋2:將分隔符緩沖對象ByteBuf傳入DelimiterBasedFrameDecoder,并指定最大長度。注釋3:指定為字符串字節(jié)流FixedLengthFrameDecoder該解碼器為固定長度解碼器,它能夠按照指定的長度對詳細(xì)進(jìn)行自動解碼。使用同樣也很簡單:同樣只需要修改服務(wù)端和客戶端對應(yīng)代碼中的nitChannel代碼即可publicvoidinitChannel(SocketChannelch)throwsExceptionch.pipeline().addLast(newFixedLengthFrameDecode

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論