<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] ## 結構詳解 ### IP數據報 TCP數據封裝在一個IP數據報中: ![](https://img.kancloud.cn/45/fe/45febb26a66610aa8743cceb565ad89d_461x162.png) #### TCP首部 TCP報文數據格式。TCP首部如果不計選項和填充字段,它通常是**20個字節**。 ![](https://img.kancloud.cn/51/39/5139d7f46e357cef4054c2b9a5490636_428x378.png) #### 源端口和目的端口 **各占2個字節**,這兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連接。有時一個IP地址和一個端口號也稱為socket(插口)。 #### 序號(起始碼) **占4個字節**,是本報文段所發送的數據項目組第一個字節的序號。在TCP傳送的數據流中,每一個字節都有一個序號。例如,一報文段的序號為300,而且數據共100字節,則下一個報文段的序號就是400;序號是32bit的無符號數,序號到達2^32-1后從0開始。(注:如何防止從0開始后序號相同的問題) #### 確認序號 **占4個字節**,是期望收到對方下次發送的數據的第一個字節的序號,也就是期望收到的下一個報文段的首部中的序號;**確認序號應該是上次已成功收到數據字節序號+1**。只有**ACK標志為1時,確認序號才有效**。 #### 數據偏移 占4比特,表示數據開始的地方離TCP段的起始處有多遠。實際上就是**TCP段首部的長度**。由于首部長度不固定,因此數據偏移字段是必要的。數據偏移以32位為長度單位,也就是4個字節,因此TCP首部的最大長度是60個字節。即偏移最大為15個長度單位=15*32位=15*4字節。 #### 保留 6比特,供以后應用,現在置為0。 #### 6個標志位比特 * URG:當URG=1時,注解此報文應盡快傳送,而不要按本來的列隊次序來傳送。與“緊急指針”字段共同應用,緊急指針指出在本報文段中的緊急數據的最后一個字節的序號,使接管方可以知道緊急數據共有多長; * ACK:只有當ACK=1時,確認序號字段才有效; * PSH:當PSH=1時,接收方應該盡快將本報文段立即傳送給其應用層; * RST:當RST=1時,表示出現連接錯誤,必須釋放連接,然后再重建傳輸連接。復位比特還來拒絕一個不法的報文段或拒絕打開一個連接; * SYN:SYN=1,ACK=0時表示請求建立一個連接,攜帶SYN標志的TCP報文段為同步報文段; * FIN:發端完成發送任務; #### 窗口 TCP通過滑動窗口的概念來進行流量控制。設想在發送端發送數據的速度很快而接收端接收速度卻很慢的情況下,為了保證數據不丟失,顯然需要進行流量控制, 協調好通信雙方的工作節奏。所謂滑動窗口,可以理解成接收端所能提供的緩沖區大小。TCP利用一個滑動的窗口來告訴發送端對它所發送的數據能提供多大的緩 沖區。窗口大小為字節數,起始于確認序號字段指明的值(這個值是接收端正期望接收的字節)。窗口大小是一個16bit字段,因而窗口大小最大為65535字節。 #### 檢驗和 檢驗和覆蓋了整個TCP報文段:TCP首部和數據。這是一個強制性的字段,一定是由發端計算和存儲,并由收端進行驗證。 **緊急指針** 只有當URG標志置1時緊急指針才有效。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最后一個字節的序號。 ## 三次握手過程 ![](https://img.kancloud.cn/30/40/304041b7c884a136f8f6e68ba782cebb_490x288.png) ①客戶端向服務器發出連接請求報文,這時報文首部中的同部位SYN=1,同時選擇一個初始序列號 seq=J ,此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,**SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。** ②TCP服務器收到請求報文后,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號是ack=J+1,同時也要為自己初始化一個序列號 seq=K,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。**這個報文也不能攜帶數據**,但是同樣要消耗一個序號。 ③TCP客戶進程收到確認后,還要向服務器給出確認。確認報文的ACK=1,ack=K+1,**\[自己的序列號seq=J+1\]**,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。 ④當服務器收到客戶端的確認后也進入ESTABLISHED狀態,此后雙方就可以開始通信了。 ### 為什么握手要“3”次? > 為了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。 在書中同時舉了一個例子,如下: > 已失效的連接請求報文段”的產生在這樣一種情況下:client發出的第一個連接請求報文段并沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認為是client再次發出的一個新的連接請求。于是就向client發出確認報文段,同意建立連接。假設不采用“三次握手”,那么只要server發出確認,新的連接就建立了。由于現在client并沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以為新的運輸連接已經建立,并一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。采用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由于收不到確認,就知道client并沒有要求建立連接。” 這就很明白了,防止了服務器端的一直等待而浪費資源 ## 四次揮手過程 ![](https://img.kancloud.cn/d6/73/d67396742757ea7471825a6a623b3321_1000x642.png) ①客戶端進程發出連接釋放報文,并且停止發送數據。釋放數據報文首部,FIN=1,其序列號為seq=u(等于前面已經傳送過來的數據的最后一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,**FIN報文段即使不攜帶數據,也要消耗一個序號。** ②服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,并且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了**,這時候處于半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整**個CLOSE-WAIT狀態持續的時間。 ③客戶端收到服務器的確認請求后,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最后的數據)。 ④服務器將最后的數據發送完畢后,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由于在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為seq=w,此時,服務器就進入了LAST-ACK(最后確認)狀態,等待客戶端的確認。 ⑤客戶端收到服務器的連接釋放報文后,必須發出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,**必須經過2??MSL(最長報文段壽命)的時間后,**當客戶端撤銷相應的TCB后,才進入CLOSED狀態。 ⑥服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。 ### 為什么需要四次握手 為了確保數據能夠完成傳輸。 關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之后,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這里的ACK報文和FIN報文多數情況下都是分開發送的。 ### 釋放連接時為什么TIME-WAIT狀態必須等待2MSL時間 MSL是Maximum Segment Lifetime英文的縮寫,中文可以譯為“報文最大生存時間”,他是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。 第一,為了保證A發送的最后一個ACK報文能夠到達B。這個ACK報文段有可能丟失,因而使處在LAST-ACK狀態的B收不到對已發送的FIN+ACK報文段的確認。B會超時重傳這個FIN+ACK報文段,而A就能在2MSL時間內收到這個重傳的FIN+ACK報文段,重置時間等待計時器(2MSL)。如果A在TIME-WAIT狀態不等待一段時間,而是在發送完ACK報文段后就立即釋放連接,就無法收到B重傳的FIN+ACK報文段,因而也不會再發送一次確認報文段。這樣,B就無法按照正常的步驟進入CLOSED狀態。 第二,A在發送完ACK報文段后,再經過2MSL時間,就可以使本連接持續的時間所產生的所有報文段都從網絡中消失。這樣就可以使下一個新的連接中不會出現這種舊的連接請求的報文段。 ### 客戶端突然掛掉了怎么辦? ????正常連接時,客戶端突然掛掉了,如果沒有措施處理這種情況,那么就會出現客戶端和服務器端出現長時期的空閑。解決辦法是在服務器端設置保活計時器,每當服務器收到客戶端的消息,就將計時器復位。超時時間通常設置為2小時。若服務器超過2小時沒收到客戶的信息,他就發送探測報文段。若發送了10個探測報文段,每一個相隔75秒,還沒有響應就認為客戶端出了故障,因而終止該連接。 ## 滑動窗口(流量控制) 滑動窗口實現了TCP流控制。首先明確滑動窗口的范疇:TCP是雙工的協議,會話的雙方都可以同時接收和發送數據。TCP會話的雙方都各自維護一個**發送窗口**和一個**接收窗口**。各自的接收窗口大小取決于應用、系統、硬件的限制(TCP傳輸速率不能大于應用的數據處理速率)。各自的發送窗口則要求取決于對端通告的接收窗口,要求相同。 滑動窗口解決的是**流量控制**的的問題,就是**如果接收端和發送端對數據包的處理速度不同**,如何讓雙方達成一致。接收端的緩存傳輸數據給應用層,但這個過程不一定是即時的,如果發送速度太快,會出現接收端數據overflow,流量控制解決的是這個問題。 ![](https://img.kancloud.cn/29/16/2916ee64eafb6ee7dc83a7094c8b4650_679x368.png) ![](https://img.kancloud.cn/0c/73/0c73dea2e7ad7733cf17badc363e3571_1024x558.png) ![](https://img.kancloud.cn/3c/16/3c16a106495b58682d53287aa5e80bc3_672x373.png) ![](https://img.kancloud.cn/cd/69/cd69ecdf865029c6dcead8174597eeb6_666x363.png) 1. 發送方沒有收到接收方發回的ACK,就不能向右滑動。假設發送方向接收方發了ABCD就滑動,只要對方沒收到A,就不能滑動,那么就會出現二者不同步的局面。 2. 滑動窗口提高了信道利用率,TCP是發送報文段為單位的,假如每發一個報文就要等ACK,那么對于大數據包,等待時間就太長了。只要發送的報文在滑動窗口里面,不用等每個ACK回來就可以向右滑動。本例中,開始接收端空著AB,只有CD,此時不能滑動;之后接收到EF和H,直接向右滑動2位,不必等G到位。 3. 窗口大小不能大于序號空間大小的一半。目的是為了不讓兩個窗口出現交迭,比如總大小為7,窗口大小都為4,接收窗口應當滑動4,但只剩3個序號,導致兩個窗口交迭。 4. 有一種情況沒出現:發送方發ABCD,接收方都收到然后向右滑動,但回復的ACK包全丟了。發送方未收到任何ACK, timeout后會重發ABCD,此時的接收方按累計確認的原則,收到ABCD后只會重發D的ACK,發送方收到后向右滑動。 ### 參考資料 [解析TCP之滑動窗口(動畫演示)](https://blog.csdn.net/yao5hed/article/details/81046945) ## 擁塞控制(**擁塞窗口**?cwnd) TCP發送方可能因為IP網絡的擁塞而被遏制,TCP擁塞控制就是為了解決這個問題(注意和TCP流量控制的區別)。 TCP擁塞控制的幾種方法:**慢啟動**,**擁塞避免**,**快重傳**和**快恢復**。 這里引入了一個擁塞窗口的概念; 擁塞窗口:**發送方維持一個叫做擁塞窗口 cwnd的狀態變量**。擁塞窗口的大小取決于網絡的擁塞程度,并且動態變化。發送方的讓自己的發送窗口=min(cwnd,接受端接收窗口大小)。 發送方控制擁塞窗口的原則是:**只要網絡沒有出現擁塞,擁塞窗口就增大一些,以便把更多的分組發送出去。但只要網絡出現擁塞,擁塞窗口就減小一些,以減少注入到網絡中的分組數**。 ### 慢啟動\\擁塞避免 **// 慢啟動 指數增長:cwnd = 1 => cwnd = 2 => cwnd = 4 => cwnd = 8** **// 達到默認ssthresh 16** **// 擁塞避免 線性增長:cwnd = 17 => cwnd = 18 => cwnd = 19 => cwnd = 20** **// 發現超時(注意是這里是超時,而后面的快重傳是3次失敗)** **// ssthresh 設為發生超時時的擁塞窗口數值的一半** **// 設置 cwnd = 1,開始慢增長** ![](https://img.kancloud.cn/9f/c6/9fc6902cfc3608a4fd2d232619bb049c_570x306.png) **慢啟動:** 當主機開始發送數據時,如果立即所大量數據字節注入到網絡,那么就有可能引起網絡擁塞,因為現在并不清楚網絡的負荷情況。因此,較好的方法是 先探測一下,即由小到大逐漸增大發送窗口,也就是說,由小到大逐漸增大擁塞窗口數值。通常在剛剛開始發送報文段時,先把擁塞窗口 cwnd 設置為一個最大報文段MSS的數值。而在每收到一個對新的報文段的確認后,把擁塞窗口增加至多一個MSS的數值。用這樣的方法逐步增大發送方的擁塞窗口 cwnd ,可以使分組注入到網絡的速率更加合理。 ![ ](images/screenshot_1566635047914.png) > 為了防止擁塞窗口cwnd增長過大引起網絡擁塞,還需要設置一個慢開始門限ssthresh狀態變量。慢開始門限ssthresh的用法如下: > 當 cwnd < ssthresh 時,使用上述的慢開始算法。 > 當 cwnd > ssthresh 時,停止使用慢開始算法而改用擁塞避免算法。 > 當 cwnd = ssthresh 時,既可使用慢開始算法,也可使用擁塞控制避免算法 **擁塞避免:**讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞窗口cwnd加1,而不是加倍。這樣擁塞窗口cwnd按線性規律緩慢增長,比慢開始算法的擁塞窗口增長速率緩慢得多。 無論在慢開始階段還是在擁塞避免階段,只要發送方判斷網絡出現擁塞(其根據就是沒有收到確認),就要把慢開始門限ssthresh設置為出現擁塞時的發送 方窗口值的一半(但不能小于2)。然后把擁塞窗口cwnd重新設置為1,執行慢開始算法。這樣做的目的就是要迅速減少主機發送到網絡中的分組數,使得發生 擁塞的路由器有足夠時間把隊列中積壓的分組處理完畢。 ### 快重傳\\快恢復 **// 但發現3重復ACK,確定丟包了** **// 開啟重傳** **// ssthresh 仍然設為發生超時時的擁塞窗口數值的一半** **// 從新ssthresh 開始,使用擁堵避免算法** ![ ](images/screenshot_1566635083055.png) **快重傳:**在超時重傳中,重點是定時器溢出超時了才認為發送的數據包丟失,**快速重傳機制,實現了另外的一種丟包評定標準,即如果我連續收到3次重復ACK**,發送方就認為這個seq的包丟失了,立刻進行重傳,這樣如果接收端回復及時的話,基本就是在重傳定時器到期之前,提高了重傳的效率。(啟動快重傳機制,重傳數據,其他數據發送數據放入隊列,待快重傳結束后再正常傳輸。) 快恢復,與快重傳配合使用的還有快恢復算法,其主要有以下兩個要點: 1. 當發送方連續收到接收方發來的三個重復確認時,就執行“乘法減小”算法,把慢開始門限ssthresh減半(這個減半指的是變成發生阻塞時的阻塞窗口大小的一半),這是為了預防網絡發生擁塞。(注意:接下來不執行慢開始算法) 2. 由于發送方現在認為網絡很可能沒有發生擁塞,因此現在不執行慢開始算法,而是把cwnd(擁塞窗口)值設置為慢開始門限減半后的值(有些版本會讓cwnd=ssthresh+3),然后開始執行擁塞避免算法,使擁塞窗口緩慢的線性增大。 ## 超時重傳 原理是在發送某一個數據以后就開啟一個計時器,在一定時間內如果沒有得到發送的數據報的ACK報文,那么就重新發送數據,直到發送成功為止。 影響超時重傳機制協議效率的一個關鍵參數是重傳超時時間(RTO,Retransmission TimeOut)。RTO的值被設置過大過小都會對協議造成不利影響。 1. RTO設長了,重發就慢,沒有效率,性能差。 2. RTO設短了,重發的就快,會增加網絡擁塞,導致更多的超時,更多的超時導致更多的重發。 連接往返時間(RTT,Round Trip Time),指發送端從發送TCP包開始到接收它的立即響應所消耗的時間。 ????RTO理論上最好是網絡 RTT 時間,但又受制于網絡距離與瞬態時延變化,所以實際上使用自適應的動態算法(例如 Jacobson 算法和 Karn 算法等)來確定超時時間。 ## 四種定時器 ### 重傳計時器 大家都知道TCP是保證數據可靠傳輸的。怎么保證呢?帶確認的重傳機制。在滑動窗口協議中,接受窗口會在連續收到的包序列中的最后一個包向接收端發送一個ACK,當網絡擁堵的時候,發送端的數據包和接收端的ACK包都有可能丟失。TCP為了保證數據可靠傳輸,就規定在重傳的“時間片”到了以后,如果還沒有收到對方的ACK,就重發此包,以避免陷入無限等待中。 當TCP發送報文段時,就創建該特定報文的重傳計時器。可能發生兩種情況: 1. 若在計時器截止時間到之前收到了對此特定報文段的確認,則撤銷此計時器。 2. 若在收到了對此特定報文段的確認之前計時器截止時間到,則重傳此報文段,并將計時器復位。 ### 堅持計時器 ????專門對付零窗口通知而設立的, ????先來考慮一下情景:發送端向接收端發送數據包知道接受窗口填滿了,然后接受窗口告訴發送方接受窗口填滿了停止發送數據。此時的狀態稱為“零窗口”狀態,發送端和接收端窗口大小均為0.直到接受TCP發送確認并宣布一個非零的窗口大小。但這個確認會丟失。我們知道TCP中,對確認是不需要發送確認的。若確認丟失了,接受TCP并不知道,而是會認為他已經完成了任務,并等待著發送TCP接著會發送更多的報文段。但發送TCP由于沒有收到確認,就等待對方發送確認來通知窗口大小。雙方的TCP都在永遠的等待著對方。 ????要打開這種死鎖,TCP為每一個鏈接使用一個持久計時器。當發送TCP收到窗口大小為0的確認時,就堅持啟動計時器。當堅持計時器期限到時,發送TCP就發送一個特殊的報文段,叫做探測報文。這個報文段只有一個字節的數據。他有一個序號,但他的序號永遠不需要確認;甚至在計算機對其他部分的數據的確認時該序號也被忽略。探測報文段提醒接受TCP:確認已丟失,必須重傳。 ????堅持計時器的值設置為重傳時間的數值。但是,若沒有收到從接收端來的響應,則需發送另一個探測報文段,并將堅持計時器的值加倍和復位。發送端繼續發送探測報文段,將堅持計時器設定的值加倍和復位,直到這個值增大到門限值(通常是60秒)為止。在這以后,發送端每個60秒就發送一個探測報文,直到窗口重新打開。 ### 保活計時器 ????????保活計時器使用在某些實現中,用來防止在兩個TCP之間的連接出現長時間的空閑。假定客戶打開了到服務器的連接,傳送了一些數據,然后就保持靜默了。也許這個客戶出故障了。在這種情況下,這個連接將永遠的處理打開狀態。 ????????要解決這種問題,在大多數的實現中都是使服務器設置保活計時器。**每當服務器收到客戶的信息,就將計時器復位。通常設置為兩小時。若服務器過了兩小時還沒有收到客戶的信息,他就發送探測報文段。若發送了10個探測報文段(每一個像個75秒)還沒有響應,就假定客戶除了故障,因而就終止了該連接。** 這種連接的斷開當然不會使用四次握手,而是直接硬性的中斷和客戶端的TCP連接。 ### 時間等待計時器 時間等待計時器是在**四次握手**的時候使用的。四次握手的簡單過程是這樣的:假設客戶端準備中斷連接,首先向服務器端發送一個FIN的請求關閉包(FIN=final),然后由established過渡到FIN-WAIT1狀態。服務器收到FIN包以后會發送一個ACK,然后自己有established進入CLOSE-WAIT.此時通信進入半雙工狀態,即留給服務器一個機會將剩余數據傳遞給客戶端,傳遞完后服務器發送一個FIN+ACK的包,表示我已經發送完數據可以斷開連接了,就這便進入LAST\_ACK階段。客戶端收到以后,發送一個ACK表示收到并同意請求,接著由FIN-WAIT2進入TIME-WAIT階段。服務器收到ACK,結束連接。此時(即客戶端發送完ACK包之后),客戶端還要等待2MSL(MSL=maxinum segment lifetime最長報文生存時間,2MSL就是兩倍的MSL)才能真正的關閉連接。 ## 粘包/拆包問題 TCP是基于字節流的,雖然應用層和TCP傳輸層之間的數據交互是大小不等的數據塊,但是TCP把這些數據塊**僅僅看成一連串無結構的字節流,**沒有邊界;另外從TCP的幀結構也可以看出,**在TCP的首部沒有表示數據長度的字段,**基于上面兩點,在使用TCP傳輸數據時,才有粘包或者拆包現象發生的可能。 ### 粘包、拆包表現形式 現在假設客戶端向服務端連續發送了兩個數據包,用packet1和packet2來表示,那么服務端收到的數據可以分為三種,現列舉如下: 第一種情況,接收端正常收到兩個數據包,即沒有發生拆包和粘包的現象,此種情況不在本文的討論范圍內。 ![](https://img.kancloud.cn/b3/75/b375591a741a465f90924342e48da0da_691x111.png) 第二種情況,接收端只收到一個數據包,由于TCP是不會出現丟包的,所以這一個數據包中包含了發送端發送的兩個數據包的信息,這種現象即為粘包。這種情況由于接收端不知道這兩個數據包的界限,所以對于接收端來說很難處理。 ![](https://img.kancloud.cn/26/65/26651e3e7627eebb5acc35eb6d91f52d_690x113.png) 第三種情況,這種情況有兩種表現形式,如下圖。接收端收到了兩個數據包,但是這兩個數據包要么是不完整的,要么就是多出來一塊,這種情況即發生了拆包和粘包。這兩種情況如果不加特殊處理,對于接收端同樣是不好處理的。 ![](https://img.kancloud.cn/bc/4a/bc4a9563c947a1687ca8150c4bad929e_690x114.png) ![](https://img.kancloud.cn/fc/a1/fca1d77a41892560cd418b96ec9e11f2_690x114.png) ### 粘包、拆包發生原因 發生TCP粘包或拆包有很多原因,現列出常見的幾點,可能不全面,歡迎補充, 1、要發送的數據大于TCP發送緩沖區剩余空間大小,將會發生拆包。 2、待發送數據大于MSS(最大報文長度),TCP在傳輸前將進行拆包。 3、要發送的數據小于TCP發送緩沖區的大小,TCP將多次寫入緩沖區的數據一次發送出去,將會發生粘包。 4、接收數據端的應用層沒有及時讀取接收緩沖區中的數據,將發生粘包。 等等。 ### 粘包、拆包解決辦法 通過以上分析,我們清楚了粘包或拆包發生的原因,那么如何解決這個問題呢?解決問題的關鍵在于如何給每個數據包添加邊界信息,常用的方法有如下幾個: 1、發送端給每個數據包添加包首部,首部中應該至少包含數據包的長度,這樣接收端在接收到數據后,通過讀取包首部的長度字段,便知道每一個數據包的實際長度了。 2、發送端將每個數據包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩沖區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。 3、可以在數據包之間設置邊界,如添加特殊符號,這樣,接收端通過這個邊界就可以將不同的數據包拆分開。 ## 參考資料 [TCP超詳細知識點整理](https://www.jianshu.com/p/8c5ccbe51f5b)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看