tcp粘包與udp丟包的原因


tcp粘包與udp丟包的原因

一,什么是tcp粘包與udp丟包

TCP是面向流的, 流要說明就像河水一樣, 只要有水, 就會一直流向低處, 不會間斷. TCP為了提高傳輸效率, 發送數據的時候, 並不是直接發送數據到網路, 而是先暫存到系統緩沖, 超過時間或者緩沖滿了, 才把緩沖區的內容發送出去, 這樣, 就可以有效提高發送效率. 所以會造成所謂的粘包, 即前一份Send的數據跟后一份Send的數據可能會暫存到緩沖當中, 然后一起發送。

UDP就不同了, 面向報文形式, 系統是不會緩沖的, 也不會做優化的, Send的時候, 就會直接Send到網絡上, 對方收不收到也不管, 所以這塊數據總是能夠能一包一包的形式接收到, 而不會出現前一個包跟后一個包都寫到緩沖然后一起Send。

但其實別想得太復雜的, TCP所謂的粘包處理, UDP所謂的丟包處理, 其實都是很簡單的.
TCP只要保證自己寫入的流是按 長度 + 內容 + 長度 + 內容 這樣就可以非常簡單的解決粘包問題, 切忌不要采用所謂的 開始標識 + 數據 + 結束標識 來分包, 適用性極低, 錯誤率極高, 除非數據都是固定有格式, 否則是不能采用這種方式的.
UDP傳送當中, 只存在丟包的可能, 收到包的時候, 肯定這個包的內容就是正確的, 很少會有錯誤的, 因為UDP本身也會用CRC32進行驗證, 還有長度驗證, 驗證不通過, 系統自動就會丟棄, 所以, 只需要去想象一種情況, 把一個蘋果切了很多塊, 然后要撿起這些塊到另外一個容器, 由另外一個人進行重組, 而這撿的過程當中, 有可能切的人撿漏了某些塊, 然后重組蘋果的人就要求切的人重新把缺失的塊撿回來. 

TCP在建立連接時有三次握手的過程,這樣就保證的連接的有效性。發包時發包完成也有反饋(對方接收完成有標記),所以tcp不存在丟包亂序的問題。但是發包過於頻繁時會出現粘包的問題。
UDP建立連接並沒有三次握手的過程,而且發送數據只是負責發送,不會有發送成功的反饋,所以當數據量大會被切分為多個小數據發包時會出現丟包現象。

 udp是放在數據幀里面傳輸的 數據幀最多放1500個字節,除去udp的報頭 一個數據幀最多發送1472個字節的udp,如果udp過大,就會被拆分成多個數據幀發送到網絡,即會造成丟包的現象。

 

二,什么是TCP粘包?怎么解決這個問題

在socket網絡編程中,都是端到端通信,由客戶端端口+服務端端口+客戶端IP+服務端IP+傳輸協議組成的五元組可以明確的標識一條連接。在TCP的socket編程中,發送端和接收端都有成對的socket。發送端為了將多個發往接收端的包,更加高效的的發給接收端,於是采用了優化算法(Nagle算法),將多次間隔較小、數據量較小的數據,合並成一個數據量大的數據塊,然后進行封包。那么這樣一來,接收端就必須使用高效科學的拆包機制來分辨這些數據。

1.Q:什么是TCP粘包問題?

TCP粘包就是指發送方發送的若干包數據到達接收方時粘成了一包,從接收緩沖區來看,后一包數據的頭緊接着前一包數據的尾,出現粘包的原因是多方面的,可能是來自發送方,也可能是來自接收方。

2.Q:造成TCP粘包的原因

(1)發送方原因

TCP默認使用Nagle算法(主要作用:減少網絡中報文段的數量),而Nagle算法主要做兩件事:

只有上一個分組得到確認,才會發送下一個分組
收集多個小分組,在一個確認到來時一起發送
Nagle算法造成了發送方可能會出現粘包問題

(2)接收方原因

TCP接收到數據包時,並不會馬上交到應用層進行處理,或者說應用層並不會立即處理。實際上,TCP將接收到的數據包保存在接收緩存里,然后應用程序主動從緩存讀取收到的分組。這樣一來,如果TCP接收數據包到緩存的速度大於應用程序從緩存中讀取數據包的速度,多個包就會被緩存,應用程序就有可能讀取到多個首尾相接粘到一起的包。

3.Q:什么時候需要處理粘包現象?

如果發送方發送的多組數據本來就是同一塊數據的不同部分,比如說一個文件被分成多個部分發送,這時當然不需要處理粘包現象
如果多個分組毫不相干,甚至是並列關系,那么這個時候就一定要處理粘包現象了

4.Q:如何處理粘包現象?

(1)發送方

對於發送方造成的粘包問題,可以通過關閉Nagle算法來解決,使用TCP_NODELAY選項來關閉算法。

(2)接收方

接收方沒有辦法來處理粘包現象,只能將問題交給應用層來處理。

(2)應用層

應用層的解決辦法簡單可行,不僅能解決接收方的粘包問題,還可以解決發送方的粘包問題。

解決辦法:循環處理,應用程序從接收緩存中讀取分組時,讀完一條數據,就應該循環讀取下一條數據,直到所有數據都被處理完成,但是如何判斷每條數據的長度呢?

1,格式化數據:每條數據有固定的格式(開始符,結束符),這種方法簡單易行,但是選擇開始符和結束符時一定要確保每條數據的內部不包含開始符和結束符。
2,發送長度:發送每條數據時,將數據的長度一並發送,例如規定數據的前4位是數據的長度,應用層在處理時可以根據長度來判斷每個分組的開始和結束位置。

5.Q:UDP會不會產生粘包問題呢?

TCP為了保證可靠傳輸並減少額外的開銷(每次發包都要驗證),采用了基於流的傳輸,基於流的傳輸不認為消息是一條一條的,是無保護消息邊界的(保護消息邊界:指傳輸協議把數據當做一條獨立的消息在網上傳輸,接收端一次只能接受一條獨立的消息)。

UDP則是面向消息傳輸的,是有保護消息邊界的,接收方一次只接受一條獨立的信息,所以不存在粘包問題。

舉個例子:有三個數據包,大小分別為2k、4k、6k,如果采用UDP發送的話,不管接受方的接收緩存有多大,我們必須要進行至少三次以上的發送才能把數據包發送完,但是使用TCP協議發送的話,我們只需要接受方的接收緩存有12k的大小,就可以一次把這3個數據包全部發送完畢。

 

三,UDP主要丟包原因及具體問題分析

一、主要丟包原因

1、 接收端處理時間過長導致丟包:調用recv方法接收端收到數據后,處理數據花了一些時間,處理完后再次調用recv方法, 在這二次調用間隔里,發過來的包可能丟失。對於這種情況可以修改接收端,將包接收后存入一個緩沖區,然后迅速返回繼續recv。
 
2、 發送的包巨大丟包:雖然send方法會幫你做大包切割成小包發送的事情,但包太大也不行。例如超過50K的一個udp包,不切割直接通過send方法發送也會導致這個包丟失。這種情況需要切割成小包再逐個send。
 
3、 發送的包較大,超過接受者緩存導致丟包:包超過mtu size數倍,幾個大的udp包可能會 超過接收者的緩沖,導致丟包。這種情況可以設置socket接收緩沖。以前遇到過這種問題,我把接收緩沖設置成64K就解決了。
int nRecvBuf=32*1024;//設置為32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
 
4、 發送的包頻率太快:雖然每個包的大小都小於mtu size 但是頻率太快,例如40多個mut size的包連續發送中間不sleep,也有可能導致丟包。這種情況也有時可以通過設置socket接收緩沖解決,但有時解決不了。所以在發送頻率過快的時候還是考慮sleep一下吧。
 
5、 局域網內不丟包,公網上丟包。這個問題我也是通過切割小包並sleep發送解決的。如果流量太大,這個辦法也不靈了。總之udp丟包總是會有的,如果出現了用我的方法解決不了,還有這個幾個方法: 要么減小流量,要么換tcp協議傳輸,要么做丟包重傳的工作。
 
 

二、具體問題分析

 
1.發送頻率過高導致丟包
 
很多人會不理解發送速度過快為什么會產生丟包,原因就是UDP的SendTo不會造成線程阻塞,也就是說,UDP的SentTo不會像TCP中的SendTo那樣,直到數據完全發送才會return回調用函數,它不保證當執行下一條語句時數據是否被發送。(SendTo方法是異步的)這樣,如果要發送的數據過多或者過大,那么在緩沖區滿的那個瞬間要發送的報文就很有可能被丟失。至於對“過快”的解釋,作者這樣說:“A few packets a second are not an issue; hundreds or thousands may be an issue.”(一秒鍾幾個數據包不算什么,但是一秒鍾成百上千的數據包就不好辦了)。 要解決接收方丟包的問題很簡單,首先要保證程序執行后馬上開始監聽(如果數據包不確定什么時候發過來的話),其次,要在收到一個數據包后最短的時間內重新回到監聽狀態,其間要盡量避免復雜的操作(比較好的解決辦法是使用多線程回調機制)。
 
2.報文過大丟包
 
至於報文過大的問題,可以通過控制報文大小來解決,使得每個報文的長度小於MTU。以太網的MTU通常是1500 bytes,其他一些諸如撥號連接的網絡MTU值為1280 bytes,如果使用speaking這樣很難得到MTU的網絡,那么最好將報文長度控制在1280 bytes以下。
 
3.發送方丟包
 
發送方丟包:內部緩沖區(internal buffers)已滿,並且發送速度過快(即發送兩個報文之間的間隔過短);  接收方丟包:Socket未開始監聽;  雖然UDP的報文長度最大可以達到64 kb,但是當報文過大時,穩定性會大大減弱。這是因為當報文過大時會被分割,使得每個分割塊(翻譯可能有誤差,原文是fragmentation)的長度小於MTU,然后分別發送,並在接收方重新組合(reassemble),但是如果其中一個報文丟失,那么其他已收到的報文都無法返回給程序,也就無法得到完整的數據了。

 

 

-------------------------下面是一個UDP丟包的例子---------------------------------------

 

UDP丟包

我們是后一個包丟掉了

最近在做一個項目,在這之前,做了個驗證程序.
發現客戶端連續發來1000個1024字節的包,服務器端出現了丟包現象.
糾其原因,是服務端在還未完全處理掉數據,客戶端已經數據發送完畢且關閉了.

有沒有成熟的解決方案來解決這個問題.
我用過sleep(1),暫時解決這個問題,但是這不是根本解決辦法,如果數據量大而多,網絡情況不太好的話,還是有可能丟失.

你試着用阻塞模式吧...
select...我開始的時候好像也遇到過..不過改為阻塞模式后就沒這個問題了...

采用回包機制,每個發包必須收到回包后再發下一個

UDP丟包是正常現象,因為它是不安全的。

丟包的原因我想並不是“服務端在還未完全處理掉數據,客戶端已經數據發送完畢且關閉了”,而是服務器端的socket接收緩存滿了(udp沒有流量控制,因此發送速度比接收速度快,很容易出現這種情況),然后系統就會將后來收到的包丟棄。你可以嘗試用setsockopt()將接收緩存(SO_RCVBUF)加大看看能不能解決問題。

服務端采用多線程pthread接包處理

UDP是無連接的,面向消息的數據傳輸協議,與TCP相比,有兩個致命的缺點,一是數據包容易丟失,二是數據包無序。
要實現文件的可靠傳輸,就必須在上層對數據丟包和亂序作特殊處理,必須要有要有丟包重發機制和超時機制。
常見的可靠傳輸算法有模擬TCP協議,重發請求(ARQ)協議,它又可分為連續ARQ協議、選擇重發ARQ協議、滑動窗口協議等等。
如果只是小規模程序,也可以自己實現丟包處理,原理基本上就是給文件分塊,每個數據包的頭部添加一個唯一標識序號的ID值,當接收的包頭部ID不是期望中的ID號,則判定丟包,將丟包ID發回服務端,服務器端接到丟包響應則重發丟失的數據包。
模擬TCP協議也相對簡單,3次握手的思想對丟包處理很有幫助。

 

udp是不安全的,如果不加任何控制,不僅會丟失包,還可能收到包的順序和發送包的順序不一樣。這個必須在自己程序中加以控制才行。
收到包后,要返回一個應答,如果發送端在一定時間內沒有收到應答,則要重發。

UDP本來存在丟包現象,現在的解決方案暫時考慮雙方增加握手.
這樣做起來,就是UDP協議里面加上了TCP的實現方法.
程序中采用的是pthread處理,丟包率時大時小,不穩定可靠

 

我感覺原因可能有兩個,一個是客戶端發送過快,網絡狀況不好或者超過服務器接收速度,就會丟包。
第二個原因是服務器收到包后,還要進行一些處理,而這段時間客戶端發送的包沒有去收,造成丟包。

解決方法:

1,客戶端降低發送速度,可以等待回包,或者加一些延遲。
2,服務器部分單獨開一個線程,去接收UDP數據,存放在一個緩沖區中,又另外的線程去處理收到的數據,盡量減少因為處理數據延時造成的丟包。

 

有兩種方法解決樓主的問題:
方法一:重新設計一下協議,增加接收確認超時重發。(推薦)
方法二:在接收方,將通信和處理分開,增加個應用緩沖區;如果有需要增加接收socket的系統緩沖區。(本方法不能從根本解決問題,只能改善)

 

網絡丟包,是再正常不過的了。
既然用UDP,就要接受丟包的現實,否則請用TCP。
如果必須使用UDP,而且丟包又是不能接受的,只好自己實現確認和重傳,說白了,就是自己實現TCP(當然是部分和有限的簡單實現)。

 

UDP是而向無連接的,用戶在實施UDP編程時,必須制定上層的協議,包括流控制,簡單的超時和重傳機制,如果不要求是實時數據,我想TCP可能會更適合你!

 

-------------------------

1:什么是丟包率? 
你的電腦向目標發送一個數據包,如果對方沒有收到.就叫丟包. 
比如你發10個,它只收到9個. 那么丟包率就是 10% 
數據在網絡中是被分成一各個個數據報傳輸的,每個數據報中有表示數據信息和提供數據路由的楨.而數據報在一般介質中傳播是總有一小部分由於兩個終端的距離過大會丟失,而大部分數據包會到達目的終端.所謂網絡丟包率是數據包丟失部分與所傳數據包總數的比值.正常傳輸時網絡丟包率應該控制在一定范圍內.

2:什么是吞吐量?
網絡中的數據是由一個個數據包組成,防火牆對每個數據包的處理要耗費資源。吞吐量是指在沒有幀丟失的情況下,設備能夠接受的最大速率。其測試方法是:在測試中以一定速率發送一定數量的幀,並計算待測設備傳輸的幀,如果發送的幀與接收的幀數量相等,那么就將發送速率提高並重新測試;如果接收幀少於發送幀則降低發送速率重新測試,直至得出最終結果。吞吐量測試結果以比特/秒或字節/秒表示。

吞吐量和報文轉發率是關系防火牆應用的主要指標,一般采用FDT(Full Duplex Throughput)來衡量,指64字節數據包的全雙工吞吐量,該指標既包括吞吐量指標也涵蓋了報文轉發率指標。 

隨着Internet的日益普及,內部網用戶訪問Internet的需求在不斷增加,一些企業也需要對外提供諸如WWW頁面瀏覽、FTP文件傳輸、DNS域名解析等服務,這些因素會導致網絡流量的急劇增加,而防火牆作為內外網之間的唯一數據通道,如果吞吐量太小,就會成為網絡瓶頸,給整個網絡的傳輸效率帶來負面影響。因此,考察防火牆的吞吐能力有助於我們更好的評價其性能表現。這也是測量防火牆性能的重要指標。

吞吐量的大小主要由防火牆內網卡,及程序算法的效率決定,尤其是程序算法,會使防火牆系統進行大量運算,通信量大打折扣。因此,大多數防火牆雖號稱100M防火牆,由於其算法依靠軟件實現,通信量遠遠沒有達到100M,實際只有10M-20M。純硬件防火牆,由於采用硬件進行運算,因此吞吐量可以達到線性90-95M,是真正的100M防火牆。

對於中小型企業來講,選擇吞吐量為百兆級的防火牆即可滿足需要,而對於電信、金融、保險等大公司大企業部門就需要采用吞吐量千兆級的防火牆產品。

3:檢測丟包率
下載一個世紀前線,在百度可以找到,很小的程序。

NetIQ Chariot  一款網絡應用軟件性能測試工具

網絡吞吐量測試,CHARIOT測試網絡吞吐量

 

原文地址:

https://www.cnblogs.com/zzt-lovelinlin/p/5303884.html
https://blog.csdn.net/weixin_41047704/article/details/85340311
https://www.cnblogs.com/Zhaols/p/6105926.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM