-
1 # Java實戰技術
-
2 # 使用者7422346175570
udp socket不存在粘包問題, 但是存在亂序, 丟包, 重複到達的情況
假設在沒丟包的情況下
又假設你的應用層緩衝區和想要取的位元組足夠大(至少長度要大於你要取出來的udp包)
udp的包可以完整乾淨的取出來, 包與包之間資料彼此獨立(不粘包), 包內部資料不會亂序
就是說你傳送端傳送了兩個包, 一個ABC三個位元組, 另外一個是DEF三個位元組, 只要沒丟包沒重複到達的情況下, 接收端收到的兩個包肯定是一個[ABC], 另外一個是[DEF]
但是tcp的socket就有可能粘包(粘包是常態)
畢竟TCP是"流協議"(stream), 是"無記錄邊界(位元組流)"的協議
仔細理解"流"的概念, 看看江河湖海里面的水流甚至你家水龍頭裡的自來水
裡面的水流(位元組)是不是連續的, 假設只有一個出口(peer)的前提下, 源源不斷的位元組流流出來, 無法斷定這一段流裡面是包含了多個數據包, 還是一個恰好完整的包, 還是隻包含了半個包 (另外的半個包得繼續等接下來的位元組流.)
並且往往想要取到後面的水流, 你得把這之前的水流都取出來才可以(有序). 常規操作下不能直接取某一段(尤其是後面)的水流.
傳送端同樣發兩個包, 一個ABC三個位元組, 另外一個是DEF三個位元組
這裡假設接收端每次核心緩衝區有資料就讀取全部資料, 且應用層緩衝區足夠大
那麼接收端
可能返回2次, [ABC][DEF], 這種情況就如同傳送端傳送的情況一樣
可能返回3次, [AB][CD][EF]
也可能返回一次[ABCDEF], 比如傳送的時候網路突然不太好, 一直在積壓, 突然網路暢通了一次都發出去了
甚至可能返回6次, [A], [B], [C], [D], [E], [F]網路斷斷續續
這就是為啥大部分tcp都要自己搞tlv(往往定義個定長的包頭, 包頭裡面帶著變長的包體長度)來定義一個應用層資料包, 這樣可以從源源不斷的流中一個接一個區分(擷取)出多個具體的資料包
回覆列表
首先,TCP是流協議,根本不存在所謂粘包一說。
簡單地說,TCP保證傳送方以什麼順序發位元組流,接收方就一定能按這個順序接收到,或者因為網路超時返回錯誤。這個是作業系統保證的,應用程式根本不用管也控制不了。
題主的問題是傳送方應該以什麼格式傳送資料,接收方能正確解析出資料,這個叫應用層協議,你自己定,跟TCP完全無關。如果是發文件,最簡單的你可以用http協議封裝,如果你發的http協議資料是100%正確的,無論哪個接收方(nginx/tomcat/iis)保證能一位元組不差地收下,因為http協議本身就帶header和body,header裡有Content-Length: 12345指定了body的大小,body才是檔案本身。
你不用http協議,直接發文件資料,那麼問題來了,接收方怎麼知道應該收多少位元組後文件結束?題主的方法是傳送方暫停0.1s這樣接收方如果0.1s沒收到那麼自己認為檔案收完了,這種方式一看就是拼機率,假定是千兆網,根本不可能適應不同網路。
還有就是文件裡明明白白說了,send和recv的返回值表示成功傳送/接收的位元組數,具體原文件說明如下:
send(2) Upon successful completion, the number of bytes which were sent is returned. Otherwise, -1 is returned and the global variable errno is set to indicate the error.
recv(2) These calls return the number of bytes received, or -1 if an error occurred.
看了文件,不僅不會產生「粘包」的錯覺,甚至處理方法都唾手可得。沒發完?繼續發呀。沒收完?繼續收呀。怎麼知道沒收完?約定個特殊內容代表結束,或者約定先發個長度不就完了嘛。怎麼?多收了?你想想是怎麼知道多少才是多的?