<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何解決粘包問題?

          共 2326字,需瀏覽 5分鐘

           ·

          2021-07-02 08:54

          ??關注“博文視點Broadview”,獲取更多書訊

          進行技術面試時,面試官經(jīng)常會問:“網(wǎng)絡通信時,如何解決粘包、丟包或者包亂序問題?”

          這其實考察的就是網(wǎng)絡基礎知識。

          如果使用 TCP 進行通信,則在大多數(shù)場景下是不存在丟包和包亂序問題的。

          因為TCP通信是可靠的通信方式,TCP棧通過序列號和包重傳確認機制保證數(shù)據(jù)包的有序和一定被正確發(fā)送到目的地;如果使用UDP進行通信,且不允許少量丟包,就要自己在UDP的基礎上實現(xiàn)類似TCP這種有序和可靠的傳輸機制了(例如RTP、RUDP)。所以將該問題拆解后,就只剩下如何解決粘包的問題。

          什么是粘包?

          粘包就是連續(xù)向對端發(fā)送兩個或者兩個以上的數(shù)據(jù)包,對端在一次收取中收到的數(shù)據(jù)包數(shù)量可能大于1個,當大于1個時,可能是幾個(包括一個)包加上某個包的部分,或者干脆幾個完整的包在一起。當然,也可能收到的數(shù)據(jù)只是一個包的部分,這種情況一般也叫作半包。

          粘包示意圖如下圖所示。

                                               

          無論是半包問題還是粘包問題,因為TCP是流式數(shù)據(jù)格式,所以其解決思路還是從收到的數(shù)據(jù)中把包與包的邊界區(qū)分出來。

          如何區(qū)分呢?

          一般有以下三種方法。

          (1)固定包長的數(shù)據(jù)包。固定包長,即每個協(xié)議包的長度都是固定的。假如我們規(guī)定每個協(xié)議包的大小都是64字節(jié),每收滿64字節(jié),就取出來解析(如果不夠,就先存起來),則這種通信協(xié)議的格式簡單但靈活性差。如果包的內容長度小于指定的字節(jié)數(shù),對剩余的空間就需要填充特殊的信息,例如\0(如果不填充特殊的內容,那么如何區(qū)分包里面的正常內容與填充信息呢);如果包的內容超過指定的字節(jié)數(shù),又得分包分片,則需要增加額外的處理邏輯——在發(fā)送端進行分包分片,在接收端重新組裝包片。

          (2)以指定的字符(串)為包的結束標志。這種協(xié)議包比較常見,即在字節(jié)流中遇到特殊的符號值時就認為到一個包的末尾了。例如 FTP 或 SMTP,在一個命令或者一段數(shù)據(jù)后面加上\r\n(即CRLF)表示一個包的結束。對端收到數(shù)據(jù)后,每遇到一個“\r\n”,就把之前的數(shù)據(jù)當作一個數(shù)據(jù)包。這種協(xié)議一般用于一些包含各種命令控制的應用中,其不足之處就是如果協(xié)議數(shù)據(jù)包的內容部分需要使用包結束標志字符,就需要對這些字符做轉碼或者轉義操作,以免被接收方錯誤地當成包結束標志而誤解析。

          (3)包頭+包體格式。這種格式的包一般分為兩部分,即包頭和包體,包頭是固定大小的,且包頭必須包含一個字段來說明接下來的包體有多大。例如:

          struct msg_header{    int32_t bodySize;    int32_t cmd;};

          就是一個典型的包頭格式,bodySize指定了這個包的包體是多大。

          由于包頭的大小是固定的(這里是size(int32_t) + sizeof(int32_t) = 8字節(jié)),所以對端先收取包頭大小的字節(jié)內容(當然,如果不夠,則還是將其先緩存起來,直到收夠為止),然后解析包頭,根據(jù)包頭中指定的包體大小收取包體,等包體收夠了,就組裝成一個完整的包來處理。

          在某些實現(xiàn)中,包頭中的bodySize可能被另一個叫作packageSize的字段代替,這個字段用于表示整個包的大小(即包頭加上包體的大小),這時,我們只要用packageSize減去包頭大小(這里是sizeof(msg_header))就能算出包體的大小,原理同上。

          在使用大多數(shù)網(wǎng)絡庫時,我們通常需要根據(jù)協(xié)議的格式自己對數(shù)據(jù)包分界和解析,一般的網(wǎng)絡庫不提供這種功能是因為需要支持不同的協(xié)議。

          由于協(xié)議的不確定性,網(wǎng)絡庫無法預先提供具體的解包代碼。當然,這不是絕對的,也有一些網(wǎng)絡庫提供了這種功能。

          在Java Netty網(wǎng)絡框架中提供了FixedLengthFrameDecoder類處理長度是定長的協(xié)議包,提供了DelimiterBasedFrameDecoder類處理將特殊字符作為結束符的協(xié)議包,提供了ByteToMessageDecoder類處理自定義格式的協(xié)議包(可用來處理包頭+包體這種格式的數(shù)據(jù)包)。

          然而,在繼承ByteToMessageDecoder的子類中,我們需要根據(jù)自己的協(xié)議的具體格式重寫decode方法對數(shù)據(jù)包進行解包。

          本文摘自《C++服務器開發(fā)精髓》一書!



          ▊《C++服務器開發(fā)精髓

          張遠龍 著


          • 從操作系統(tǒng)原理角度講解C++服務器開發(fā)技術棧

          • 內容詳盡細致、版本新

          • 重磅級C++服務器開發(fā)紅寶書


          本書詳細講解如何掌握C++服務器開發(fā)技術,以及如何成為合格的C++開發(fā)者,秉承的思想是,通過掌握技術原理,可以輕松制造“輪子”,靈活設計出優(yōu)雅、魯棒的服務,并快速學習新技術。

          無論是對于C/C++開發(fā)者、計算機專業(yè)的學生,還是對于想了解操作系統(tǒng)原理的讀者,本書都極具參考價值。


          (掃碼了解本書詳情)





          如果喜歡本文
          歡迎 在看留言分享至朋友圈 三連


           熱文推薦  





          ▼點擊閱讀原文,查看本書詳情~
          瀏覽 20
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩蜜桃久久久 | 亲子乱AV一区二区 | 超碰在线91 | 91一级A片在线观看 | 黄色大片免费看 |