使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

在上一篇文章中主要是使用Springboot開發了一個Netty通信的基本案例,這篇文章是在上一篇文章的基礎之上進行講解的,主要是考慮傳輸數據如果遇到粘包問題該如何解決。

這篇文章會按照以下步驟進行講解,希望對你有所收穫:

1、什麼是TCP粘包拆包2、Netty中粘包問題的問題重現3、Netty中粘包問題的解決方案

OK,在你心中有這麼一個基本的脈絡之後就可以開始今天的文章了。本系列所有的文章都會給出完整的代碼,且在電腦上真實運行了一遍,確保無誤。

一、什麼是TCP拆包和粘包

我們使用TCP協議在傳輸數據的時候,如果數據塊比較大,就會考慮將其切分。把一個大的數據包進行切割成一個個小的數據包發送。這時候就會遇到拆包和粘包的問題。

比如說在這裡客戶端發送了兩個數據包D1和D2到服務端,在傳輸的時候就可能會遇到下列問題:

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

通過上面這張圖相信你基本上能夠理解了。不過我們在這裡還是需要稍微解釋一下:

情況1:D1和D2正常發送,每次發送一個整包。

情況2:D1數據包比較大,D2比較小。第一次發送D1的一部分,第二次發送D1剩下的和D2整包。這叫拆包。

情況2:D1和D2數據包都比較小,一次發送兩個整包,這就叫做粘包。

情況4:D1數據包比較小,D2比較大。第一次發送D1整包和D2一部分,第二次發送D2剩下的。這叫拆包。

情況5:D1和D2數據包都比較大,這時候分開發。

為什麼會出現這樣的問題呢?想要解釋清楚,就必須要考慮到計算機網絡的相關知識了,TCP在接受數據的時候,有一個滑動窗口來控制接受數據的大小,這個滑動窗口你就可以理解為一個緩衝區的大小。緩衝區滿了就會把數據發送。數據包的大小是不固定的,有時候比緩衝區大,有時候小。這時候就會出現上面的現象。

下面我們使用代碼來重現這個現象。

二、問題重現

1、前提準備

我們是基於Springboot開發的,因此還是和上一節一樣,首先創建一個Springboot的web工程,添加一下依賴:

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

如果你沒有使用maven,下載相關jar包,直接導入IDE中即可。

2、服務端代碼開發

步驟一:創建server類

這個server類,在上一篇文章中提到,是一個模板類,直接拿來用即可。

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

在上面的這個代碼中同樣我們最主要的是關注ServerUAVHandler的實現。

步驟二:Handler的實現

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

在這個類中,使用channelRead方法來讀取客戶端發送過來的信息。

(1)首先定義了一個counter,用於計算客戶端發送了多少條消息。

(2)在channelRead內部,首先將msg轉化為ByteBuf。

(3)將buf的數據轉化為字節byte

(4)將buf的字節數據轉化為String類型,然後輸出。

(5)使用ctx的writeAndFlush方法,每收到一個客戶端的數據,給對方回覆一個A。別忘了還有一個換行符。

在上面的這個代碼中,最主要的就是服務端每收到一條客戶端的信息,就給其回覆一條。也就是說客戶端和服務端的消息數量應該是一樣的。

3、客戶端代碼開發

步驟一:創建client類

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

同樣的代碼的邏輯在上一篇文章中已經說了,我們還是最關注的事件處理類Handler。

步驟二:Handler實現

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

這個客戶端的Handler看起來有點多,一共有兩個方法,channelActive和channelRead。

(1)channelActive裡面使用for循環給服務器發送了100條,我愛你。每次發送還有在末尾添加一個換行符。

(2)channelRead裡面接受服務器返回的消息。

按道理來講,客戶端給服務端發送了100條數據,那麼服務端也會返回回來100條。我們來驗證一下。

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

這裡輸出的是服務端的信息,從上面的輸出結果你就會發現,其實客戶端的“我愛你”都被黏在了一塊。本來100條但是現在卻只有17條了,這就是發生了粘包現象。

如何來解決呢?下面我們看看。

三、粘包問題解決

解決的思路很簡單,也就是每次發送一個數據包的時候,添加一個標識符,讀的時候一直讀到這個標識符才表示一個完整的數據包。在上面我們添加的是line.separator,也就是換行符“\\n”。

1、服務端server類更改。

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

2、服務端Handler類更改

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

3、客戶端Client更改

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

4、客戶端Handler更改

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

客戶端和服務端改的地方都一樣,不過還是貼了出來,現在我們再運行一波。

使用Netty通信時,遇到TCP粘包拆包問題如何解決?答案如此簡單

看到沒是不是很神奇。我們來分析一下我們都修改了什麼。

好像我們就只是在server和client類添加了兩個類,一個是LineBasedFrameDecoder,一個是StringDecoder,其他的都是直接刪除,這兩個類有什麼作用呢?

(1)LineBasedFrameDecoder的作用是在讀取數據的時候,一直讀到是否含有換行符“\\n”或者是“\\r\\n”。如果讀到了就表示該結束了。因此就拿到了這一行的數據包。

(2)StringDecoder用於對之前LineBasedFrameDecoder讀取的這一行數據包進行解碼。將對象轉換為字符串。

OK,好像他們倆搭配,幹活真不累,現在我們終於可以解決粘包的問題了,但是同時也出現了一個新的問題,那就是如果我們的標識符不是換行符“\\n”或者是“\\r\\n”又該怎麼辦呢?幸好Netty同樣為我們提供了幾種其他的解碼器,叫做DelimiterBasedFrameDecoder和FixedLengthFrameDecoder,前面這個可以自動完成以分隔符做結束標誌的消息,後面這個可以自動完成對定長消息的解碼。都可以解決粘包拆包問題。

現在我們解決了一個大問題,但是同時也出現了一個新的大問題,那就是我們的數據都是通過網絡進行傳輸的,因此就需要編碼和解碼的操作。netty又為我們提供了什麼編碼解碼技術呢?下篇文章更精彩。感謝大家。


分享到:


相關文章: