第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Netty 粘包和拆包

1. 前言

前面幾個(gè)章節(jié)主要解析了 Netty 的編碼、解碼問(wèn)題,那么是否有了編解碼器,我們的 Netty 通信就能正常了呢?

TCP 協(xié)議在傳輸數(shù)據(jù)時(shí)沒(méi)有辦法判斷數(shù)據(jù)是什么時(shí)候結(jié)束的,它無(wú)法識(shí)別一段完整的信息,因此可能會(huì)導(dǎo)致接受到的數(shù)據(jù)和發(fā)送時(shí)的數(shù)據(jù)不一致的情況。因此需要人為的指定一種規(guī)范的協(xié)議,從而保證數(shù)據(jù)的安全性,比如:我們所熟悉的 HTTP 協(xié)議。

本節(jié)內(nèi)容,我們主要需要以下兩點(diǎn)知識(shí)

  1. TCP 拆包、粘包的原因;
  2. TCP 拆包、粘包的解決方案。

2. 學(xué)習(xí)目的

拆包、粘包在 TCP 協(xié)議當(dāng)中,或者說(shuō) Netty 開(kāi)發(fā)當(dāng)中必須需要去解決的問(wèn)題。在開(kāi)發(fā)當(dāng)中,你會(huì)發(fā)現(xiàn)你不需要解決拆包、粘包問(wèn)題,數(shù)據(jù)也是能正常發(fā)送和接受,那么為什么需要去解決呢?

原因是,數(shù)據(jù)量比較小,TCP 發(fā)送之前它是有個(gè)緩沖池的,根據(jù)緩沖池的大小來(lái)把數(shù)據(jù)包拆分成多個(gè)小包進(jìn)行發(fā)送。在高并發(fā)的情況下,拆包、粘包問(wèn)題是經(jīng)常會(huì)發(fā)生的,因此需要去 解決,否則接收方將獲取不到正確的數(shù)據(jù)。

3. 粘包和拆包問(wèn)題解析

3.1 模擬拆包粘包問(wèn)題

開(kāi)始,之前我們先看一個(gè)簡(jiǎn)單的案例,具體如下所示:

客戶端: 客戶端使用 for 循環(huán),連續(xù)向服務(wù)端發(fā)送 hello world1000 遍(使用 StringEncoder 編碼器)。

public class ClientTestHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {     
        for(int i=0;i<1000;i++){
            ctx.channel().writeAndFlush(
                Unpooled.copiedBuffer("hello world 世界你好,Netty技術(shù)學(xué)習(xí)".getBytes())
            );
        }
    }
}

服務(wù)端: 正常輸出客戶端的信息(使用 StringDecoder 解碼器)。

public class ServerTestHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String str=msg.toString();
        System.out.println(str);
    }
}

輸出結(jié)果:
圖片描述

總結(jié):
通過(guò)以上的輸出結(jié)果,我們發(fā)現(xiàn),客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù),有時(shí)候能正確打印,有時(shí)候數(shù)據(jù)粘在了一起。以上輸出結(jié)果有亂碼想象、有多個(gè)信息輸出到一行,就是 ByteBuf 粘包和 ByteBuf 半包。

通過(guò)上面的簡(jiǎn)單案例,我們發(fā)現(xiàn) TCP 協(xié)議下會(huì)產(chǎn)生數(shù)據(jù)安全性問(wèn)題,其實(shí)在 TCP 中粘包和拆包是不可避免的,因?yàn)樵?TCP 協(xié)議中,數(shù)據(jù)流向水流一樣,根本不知道應(yīng)該從哪里截取才是完整的數(shù)據(jù)包。TCP 并不了解上層業(yè)務(wù)的數(shù)據(jù)含義,它會(huì)根據(jù) TCP 緩沖區(qū)的實(shí)際情況進(jìn)行包的劃分,因此一個(gè)完整的業(yè)務(wù)包可能會(huì)被 TCP 拆分成多個(gè)包進(jìn)行發(fā)送,也可能會(huì)把多個(gè)小包封裝成一個(gè)大包進(jìn)行發(fā)送,這就是 TCP 粘包和拆包問(wèn)題。

3.2 常見(jiàn)的原因分析

粘包和拆包其實(shí)是客戶端和服務(wù)端之間都會(huì)發(fā)生的事情,并不是說(shuō)只是在客戶端產(chǎn)生或者服務(wù)端產(chǎn)生,具體分析如下:

發(fā)送方的粘包和拆包問(wèn)題

  1. 要發(fā)送的數(shù)據(jù)大于 TCP 發(fā)送緩沖區(qū)剩余空間大小,將會(huì)發(fā)生拆包,也就是拆分幾次發(fā)送;
  2. 要發(fā)送數(shù)據(jù)大于最大報(bào)文長(zhǎng)度,TCP 在傳輸前將進(jìn)行拆包,也就是拆分幾次發(fā)送;
  3. 要發(fā)送的數(shù)據(jù)小于 TCP 發(fā)送緩沖區(qū)的大小,TCP 將多次寫入緩沖區(qū)的數(shù)據(jù)一次發(fā)送出去,將會(huì)發(fā)生粘包。

接收方的粘包和拆包問(wèn)題

  1. 服務(wù)端分兩次讀取到獨(dú)立的數(shù)據(jù)包,那么解析出來(lái)的數(shù)據(jù)正常,沒(méi)有粘包和拆包問(wèn)題;
  2. 服務(wù)端一次讀取兩個(gè)數(shù)據(jù)包,那么這些數(shù)據(jù)包就會(huì)粘合在一起,因此稱為粘包;
  3. 服務(wù)端分兩次讀取兩個(gè)數(shù)據(jù)包,第一次讀到數(shù)據(jù) 1 和數(shù)據(jù) 2 部分內(nèi)容,第二次讀取數(shù)據(jù) 2 剩余內(nèi)容,這被成為 TCP 拆包。

粘包和拆包的示意圖

圖片描述

總結(jié),拆包和粘包問(wèn)題并不是某一方的問(wèn)題,可能是發(fā)送的粘包和拆包導(dǎo)致接收方讀取數(shù)據(jù)出錯(cuò),也可能是發(fā)送方正常,但是接收方讀取出錯(cuò)。但是我們只需要了解,發(fā)送方和接收方什么情況下會(huì)拆包和粘包。

4. Netty 提供的粘包拆包解決方案

雖然,在 Netty 當(dāng)中是基于 ByteBuf 字節(jié)容器去編程,但是底層還是會(huì)被轉(zhuǎn)換成字節(jié)流進(jìn)行傳輸, 數(shù)據(jù)到了服務(wù)端,也是按照字節(jié)流的方式讀入,然后到了 Netty 應(yīng)用層面,重新拼裝成 ByteBuf。如果為了數(shù)據(jù)的完整性,通常的解決方案如下:

  1. 每次讀取完都需要判斷是否是一個(gè)完整數(shù)據(jù)包 ;
  2. 如果當(dāng)前讀取的數(shù)據(jù)不足以拼接成一個(gè)完整數(shù)據(jù)包,那就保留該數(shù)據(jù),繼續(xù)從 TCP 緩沖器讀取,直到拼接成一個(gè)完整數(shù)據(jù)包為止;
  3. 如果拼接成了完整的數(shù)據(jù)包,但是有多余的數(shù)據(jù),則仍然保留,以便和下次讀取的數(shù)據(jù)進(jìn)行拼接。

思考:那么應(yīng)該如何去判斷一個(gè)業(yè)務(wù)數(shù)據(jù)的完整結(jié)束呢?

方案一: 固定數(shù)據(jù)長(zhǎng)度,客戶端在發(fā)送數(shù)據(jù)的時(shí)候,每個(gè)數(shù)據(jù)包的長(zhǎng)度固定(比如:1024 個(gè)字節(jié)),如果發(fā)送數(shù)據(jù)不足 1024 字節(jié)時(shí),以空格補(bǔ)齊;服務(wù)端則每次讀取固定長(zhǎng)度是數(shù)據(jù);
方案二: 分隔符,每個(gè)數(shù)據(jù)包的結(jié)尾加一個(gè)特殊分隔符,服務(wù)端則讀取到特殊分隔符則認(rèn)為數(shù)據(jù)包結(jié)束;如果一次讀取的數(shù)據(jù)沒(méi)有結(jié)束符,則保留當(dāng)前數(shù)據(jù),等待下次讀取;
方案三: 將數(shù)據(jù)分為消息頭和消息體,在頭部保存了消息的數(shù)據(jù)長(zhǎng)度,只有讀取指定長(zhǎng)度的數(shù)據(jù)就算完整數(shù)據(jù)包;
方案四: 自定義協(xié)議,通過(guò)協(xié)議的規(guī)范進(jìn)行發(fā)送和接受數(shù)據(jù)。

當(dāng)然,以上的方案 Netty 官方也考慮到了,并且為了簡(jiǎn)化開(kāi)發(fā)人員的工作量,Netty 內(nèi)置了常見(jiàn)的拆包器,具體如下:

1. 固定長(zhǎng)度的拆包器 FixedLengthFrameDecoder

每個(gè)數(shù)據(jù)包的長(zhǎng)度都是固定的,比如 1024,那么只需要把這個(gè)拆包器加到 pipeline 中,Netty 會(huì)把一個(gè)個(gè)長(zhǎng)度為 1024 的數(shù)據(jù)包 (ByteBuf) 傳遞到下一個(gè) channelHandler。

2. 行拆包器 LineBasedFrameDecoder

它是一個(gè)特殊的分隔符拆包器,以換行符作為結(jié)束符。

3. 分隔符拆包器 DelimiterBasedFrameDecoder

可以自定義自己的分隔符。

4. 基于長(zhǎng)度域拆包器 LengthFieldBasedFrameDecoder

是最通用的一種拆包器,有一個(gè)存放數(shù)據(jù)長(zhǎng)度的字段,讀到該字段之后,往后面的數(shù)據(jù)讀取一定長(zhǎng)度的數(shù)據(jù)即可,只要你的自定義協(xié)議中包含長(zhǎng)度域字段,均可以使用這個(gè)拆包器來(lái)實(shí)現(xiàn)應(yīng)用層拆包。

5. 小結(jié)

本節(jié)內(nèi)容需要掌握的知識(shí)點(diǎn)

  1. 什么是拆包、粘包問(wèn)題,以及它的產(chǎn)生原因是什么?
  2. 解決拆包、粘包問(wèn)題的思路以及常見(jiàn)解決方案是什么?