網絡中出現TCP、UDP粘包、分包的兩點解決辦法

粘包產生原因:

先說TCP:由於TCP協議本身的機制(面向連接的可靠地協議-三次握手機制)客戶端與服務器會維持一個連接(Channel),數據在連接不斷開的情況下,可以持續不斷地將多個數據包發往服務器,但是如果發送的網絡數據包太小,那麼他本身會啓用Nagle算法(可配置是否啓用)對較小的數據包進行合併(基於此,TCP的網絡延遲要UDP的高些)然後再發送(超時或者包大小足夠)。那麼這樣的話,服務器在接收到消息(數據流)的時候就無法區分哪些數據包是客戶端自己分開發送的,這樣產生了粘包;服務器在接收到數據庫後,放到緩衝區中,如果消息沒有被及時從緩存區取走,下次在取數據的時候可能就會出現一次取出多個數據包的情況,造成粘包現象(確切來講,對於基於TCP協議的應用,不應用包來描述,而應 用 流來描述),個人認爲服務器接收端產生的粘包應該與linux內核處理socket的方式 select輪詢機制的線性掃描頻度無關。

再說UDP:本身作爲無連接的不可靠的傳輸協議(適合頻繁發送較小的數據包),他不會對數據包進行合併發送(也就沒有Nagle算法之說了),他直接是一端發送什麼數據,直接就發出去了,既然他不會對數據合併,每一個數據包都是完整的(數據+UDP頭+IP頭等等發一次數據封裝一次)也就沒有粘包一說了。

 

分包產生的原因就簡單的多:可能是IP分片傳輸導致的,也可能是傳輸過程中丟失部分包導致出現的半包,還有可能就是一個包可能被分成了兩次傳輸,在取數據的時候,先取到了一部分(還可能與接收的緩衝區大小有關係),總之就是一個數據包被分成了多次接收。

 

解決辦法:

 

粘包與分包的處理方法:

 

我根據現有的一些開源資料做了如下總結(常用的解決方案):

一個是採用分隔符的方式,即我們在封裝要傳輸的數據包的時候,採用固定的符號作爲結尾符(數據中不能含結尾符),這樣我們接收到數據後,如果出現結尾標識,即人爲的將粘包分開,如果一個包中沒有出現結尾符,認爲出現了分包,則等待下個包中出現後 組合成一個完整的數據包,這種方式適合於文本傳輸的數據,如採用/r/n之類的分隔符;

 

另一種是採用在數據包中添加長度的方式,即在數據包中的固定位置封裝數據包的長度信息(或可計算數據包總長度的信息),服務器接收到數據後,先是解析包長度,然後根據包長度截取數據包(此種方式常出現於自定義協議中),但是有個小問題就是如果客戶端第一個數據包數據長度封裝的有錯誤,那麼很可能就會導致後面接收到的所有數據包都解析出錯(由於TCP建立連接後流式傳輸機制),只有客戶端關閉連接後重新打開纔可以消除此問題,我在處理這個問題的時候對數據長度做了校驗,會適時的對接收到的有問題的包進行人爲的丟棄處理(客戶端有自動重發機制,故而在應用層不會導致數據的不完整性);

 

另一種不建議的方式是TCP採用短連接處理粘包(這個得根據需要來,所以不建議);

/***********************************************************************************************************************************************/

TCP粘包處理-RingBuf方法

TCP粘包是指發送方發送的若干包數據到接收方接收時粘成一包,從接收緩衝區看,後一包數據的頭緊接着前一包數據的尾。粘包可能由發送方造成,也可能由接收方造成。TCP爲提高傳輸效率,發送方往往要收集到足夠多的數據後才發送一包數據,造成多個數據包的粘連。如果接收進程不及時接收數據,已收到的數據就放在系統接收緩衝區,用戶進程讀取數據時就可能同時讀到多個數據包。因爲系統傳輸的數據是帶結構的數據,需要做分包處理。

爲了適應高速複雜網絡條件,我們設計實現了粘包處理模塊,由接收方通過預處理過程,對接收到的數據包進行預處理,將粘連的包分開。爲了方便粘包處理,提高處理效率,在接收環節使用了環形緩衝區來存儲接收到的數據。其結構如表1所示。

                                                            1 環形緩衝結構

字段名

類型

含義

CS

CRITICAL_SECTION

保護環形緩衝的臨界區

pRingBuf

UINT8*

緩衝區起始位置

pRead

UINT8*

當前未處理數據的起始位置

pWrite

UINT8*

當前未處理數據的結束位置

pLastWrite

UINT8*

當前緩衝區的結束位置

環形緩衝跟每個TCP套接字綁定。在每個TCP的SOCKET_OBJ創建時,同時創建一個PRINGBUFFER結構並初始化。這時候,pRingBuf指向環形緩衝區的內存首地址,pRead、pWrite指針也指向它。pLastWrite指針在這時候沒有實際意義。初始化之後的結構如圖1所示。

1 初始化後的環形緩衝區

在每次投遞一個TCP的接收操作時,從RINGBUFFER獲取內存作接收緩衝區,一般規定一個最大值L1作爲可以寫入的最大數據量。這時把pWrite的值賦給BUFFER_OBJ的buf字段,把L1賦給bufLen字段。這樣每次接收到的數據就從pWrite開始寫入緩衝區,最多寫入L1字節,如圖 2。

圖2 分配緩衝後的環形緩衝

如果某次分配過程中,pWrite到緩衝區結束的位置pEnd長度不夠最小分配長度L1,爲了提高接收效率,直接廢棄最後一段內存,標記pLastWrite爲pWrite。然後從pRingBuf開始分配內存,如圖 3。

圖 3 使用到結尾的環形緩衝

特殊情況下,如果處理包速度太慢,或者接收太快,可能導致未處理包占用大部分緩衝區,沒有足夠的緩衝區分配給新的接收操作,如圖4。這時候直接報告錯誤即可。

圖 4沒有足夠接收緩衝的環形緩衝

當收到一個長度爲L數據包時,需要修改緩衝區的指針。這時候已經寫入數據的位置變爲(pWrite+L),如圖 5。

圖 5收到長度爲L的數據的環形緩衝

分析上述環形緩衝的使用過程,收到數據後的情況可以簡單歸納爲兩種:pWrite>pRead,接收但未處理的數據位於pRead到pWrite之間的緩衝區;pWrite<pRead,這時候,數據位於pRead到pLastWrite和pRingbuf到pWrite之間。這兩種情況分別對應圖6、圖 7。

首先分析圖6。此時,pRead是一個包的起始位置,如果L1足夠一個包頭長度,就獲取該包的長度信息,記爲L。假如L1>L,就說明一個數據包接收完成,根據包類型處理包,然後修改pRead指針,指向下一個包的起始位置(pRead+L)。這時候仍然類似於之前的狀態,於是解包繼續,直到L1不足一個包的長度,或者不足包頭長度。這時退出解包過程,等待後續的數據到來。

圖 6有未處理數據的環形緩衝(1)

圖 7有未處理數據的環形緩衝(2)

圖 8稍微複雜。首先按照上述過程處理L1部分。存在一種情況,經過若干個包處理之後,L1不足一個包,或者不足一個包頭。如果這時(L1+L2)足夠一個包的長度,就需要繼續處理。另外申請一個最大包長度的內存區pTemp,把L1部分和L2的一部分複製到pTemp,然後執行解包過程。

經過上述解包之後,pRead就轉向pRingBuf到pWrite之間的某個位置,從而回歸情況圖 6,繼續按照圖 6部分執行解包。

一  粘包問題概述

二  粘包迴避設計

 

第一章  粘包問題概述

 

1.1  描述背景

採用TCP協議進行網絡數據傳送的軟件設計中,普遍存在粘包問題。這主要是由於現代操作系統的網絡傳輸機制所產生的。我們知道,網絡通信採用的套接字(socket)技術,其實現實際是由系統內核提供一片連續緩存(流緩衝)來實現應用層程序與網卡接口之間的中轉功能。多個數據包被連續存儲於連續的緩存中,在對數據包進行讀取時由於無法確定發生方的發送邊界,而採用某一估測值大小來進行數據讀出,若雙方的size不一致時就會使數據包的邊界發生錯位,導致讀出錯誤的數據分包,進而曲解原始數據含義。

 

1.2  粘包的概念

粘包問題的本質就是數據讀取邊界錯誤所致,通過下圖可以形象地理解其現象。

如圖1所示,當前的socket緩存中已經有6個數據分組到達,其大小如圖中數字。而應用程序在對數據進行收取時(如圖2),採用了300字節的要求去讀取,則會誤將pkg1和pkg2一起收走當做一個包來處理。而實際上,很可能pkg1是一個文本文件的內容,而pkg2則可能是一個音頻內容,這風馬牛不相及的兩個數據包卻被揉進一個包進行處理,顯然有失妥當。嚴重時可能因爲丟了pkg2而導致軟件陷入異常分支產生烏龍事件。

因此,粘包問題必須引起所有軟件設計者(項目經理)的高度重視!

那麼,或許會有讀者發問,爲何不讓接收程序按照100字節來讀取呢?我想如果您瞭解一些TCP編程的話就不會有這樣的問題。網絡通信程序中,數據包通常是不能確定大小的,尤其在軟件設計階段無法真的做到確定爲一個固定值。比如聊天軟件客戶端若採用TCP傳輸一個用戶名和密碼到服務端進行驗證登陸,我想這個數據包不過是幾十字節,至多幾百字節即可發送完畢,而有時候要傳輸一個很大的視頻文件,即使分包發送也應該一個包在幾千字節吧。(據說,某國電信平臺的MW中見到過一次發送1.5萬字節的電話數據)這種情況下,發送數據的分包大小無法固定,接收端也就無法固定。所以一般採用一個較爲合理的預估值進行輪詢接收。(網卡的MTU都是1500字節,因此這個預估值一般爲MTU的1~3倍)。

相信讀者對粘包問題應該有了初步認識了。

 

第二章  粘包迴避設計

 

2.0  閒扯

作者在此提出三種可解之法,這都是從軟件設計的角度去考慮的,當然代碼實現也是可以驗證沒問題的。下面一一爲讀者解開其謎底。

讀者在別的文獻中還能看到一種叫做【短連接】的方法,根據經驗不建議採用此法,開銷太大得不償失。故而本文對該方案不做解釋。

 

2.1  設計方案一:定長髮送

在進行數據發送時採用固定長度的設計,也就是無論多大數據發送都分包爲固定長度(爲便於描述,此處定長爲記爲LEN),也就是發送端在發送數據時都以LEN爲長度進行分包。這樣接收方都以固定的LEN進行接收,如此一來發送和接收就能一一對應了。分包的時候不一定能完整的恰好分成多個完整的LEN的包,最後一個包一般都會小於LEN,這時候最後一個包可以在不足的部分填充空白字節。

當然,這種方法會有缺陷。1.最後一個包的不足長度被填充爲空白部分,也即無效字節序。那麼接收方可能難以辨別這無效的部分,它本身就是爲了補位的,並無實際含義。這就爲接收端處理其含義帶來了麻煩。當然也有解決辦法,可以通過增添標誌位的方法來彌補,即在每一個數據包的最前面增加一個定長的報頭,然後將該數據包的末尾標記一併發送。接收方根據這個標記確認無效字節序列,從而實現數據的完整接收。2.在發送包長度隨機分佈的情況下,會造成帶寬浪費。比如發送長度可能爲 1,100,1000,4000字節等等,則都需要按照定長最大值即4000來發送,數據包小於4000字節的其他包也會被填充至4000,造成網絡負載的無效浪費。

綜上,此方案適在發送數據包長度較爲穩定(趨於某一固定值)的情況下有較好的效果。

 

2.2  設計方案二:尾部標記序列

在每個要發送的數據包的尾部設置一個特殊的字節序列,此序列帶有特殊含義,跟字符串的結束符標識”\0”一樣的含義,用來標示這個數據包的末尾,接收方可對接收的數據進行分析,通過尾部序列確認數據包的邊界。

這種方法的缺陷較爲明顯:1.接收方需要對數據進行分析,甄別尾部序列。2.尾部序列的確定本身是一個問題。什麼樣的序列可以向”\0”一樣來做一個結束符呢?這個序列必須是不具備通常任何人類或者程序可識別的帶含義的數據序列,就像“\0”是一個無效字符串內容,因而可以作爲字符串的結束標記。那普通的網絡通信中,這個序列是什麼呢?我想一時間很難找到恰當的答案。

 

2.3  設計方案三:頭部標記分步接收

這個方法是作者有限學識裏最好的辦法了。它既不損失效率,還完美解決了任何大小的數據包的邊界問題。

這個方法的實現是這樣的,定義一個用戶報頭,在報頭中註明每次發送的數據包大小。接收方每次接收時先以報頭的size進行數據讀取,這必然只能讀到一個報頭的數據,從報頭中得到該數據包的數據大小,然後再按照此大小進行再次讀取,就能讀到數據的內容了。這樣一來,每個數據包發送時都封裝一個報頭,然後接收方分兩次接收一個包,第一次接收報頭,根據報頭大小第二次才接收數據內容。(此處的data[0]的本質是一個指針,指向數據的正文部分,也可以是一篇連續數據區的起始位置。因此可以設計成data[user_size],這樣的話。)

下面通過一個圖來展現設計思想。

 

由圖看出,數據發送多了封裝報頭的動作;接收方將每個包的接收拆分成了兩次。

這方案看似精妙,實則也有缺陷:1.報頭雖小,但每個包都需要多封裝sizeof(_data_head)的數據,積累效應也不可完全忽略。2.接收方的接收動作分成了兩次,也就是進行數據讀取的操作被增加了一倍,而數據讀取操作的recv或者read都是系統調用,這對內核而言的開銷是一個不能完全忽略的影響,對程序而言性能影響可忽略(系統調用的速度非常快)。

優點:避免了程序設計的複雜性,其有效性便於驗證,對軟件設計的穩定性要求來說更容易達標。綜上,方案三乃上上策!

 

補充:

 

二。什麼時候需要考慮粘包問題?

1:如果利用tcp每次發送數據,就與對方建立連接,然後雙方發送完一段數據後,就關閉連接,這樣就不會出現粘包問題(因爲只有一種包結構,類似於http協議)。關閉連接主要要雙方都發送close連接(參考tcp關閉協議)。如:A需要發送一段字符串給B,那麼A與B建立連接,然後發送雙方都默認好的協議字符如"hello give me sth abour yourself",然後B收到報文後,就將緩衝區數據接收,然後關閉連接,這樣粘包問題不用考慮到,因爲大家都知道是發送一段字符。
2:如果發送數據無結構,如文件傳輸,這樣發送方只管發送,接收方只管接收存儲就ok,也不用考慮粘包
3:如果雙方建立連接,需要在連接後一段時間內發送不同結構數據,如連接後,有好幾種結構:
 1)"hello give me sth abour yourself" 
 2)"Don't give me sth abour yourself" 
   那這樣的話,如果發送方連續發送這個兩個包出去,接收方一次接收可能會是"hello give me sth abour yourselfDon't give me sth abour yourself" 這樣接收方就傻了,到底是要幹嘛?不知道,因爲協議沒有規定這麼詭異的字符串,所以要處理把它分包,怎麼分也需要雙方組織一個比較好的包結構,所以一般可能會在頭加一個數據長度之類的包,以確保接收。
 

三 .粘包出現原因:在流傳輸中出現,UDP不會出現粘包,因爲它有消息邊界(參考Windows 網絡編程)
1 發送端需要等緩衝區滿才發送出去,造成粘包
2 接收方不及時接收緩衝區的包,造成多個包接收

解決辦法:
爲了避免粘包現象,可採取以下幾種措施。一是對於發送方引起的粘包現象,用戶可通過編程設置來避免,TCP提供了強制數據立即傳送的操作指令push,TCP軟件收到該操作指令後,就立即將本段數據發送出去,而不必等待發送緩衝區滿;二是對於接收方引起的粘包,則可通過優化程序設計、精簡接收進程工作量、提高接收進程優先級等措施,使其及時接收數據,從而儘量避免出現粘包現象;三是由接收方控制,將一包數據按結構字段,人爲控制分多次接收,然後合併,通過這種手段來避免粘包。

以上提到的三種措施,都有其不足之處。第一種編程設置方法雖然可以避免發送方引起的粘包,但它關閉了優化算法,降低了網絡發送效率,影響應用程序的性能,一般不建議使用。第二種方法只能減少出現粘包的可能性,但並不能完全避免粘包,當發送頻率較高時,或由於網絡突發可能使某個時間段數據包到達接收方較快,接收方還是有可能來不及接收,從而導致粘包。第三種方法雖然避免了粘包,但應用程序的效率較低,對實時應用的場合不適合。

 

 

一.爲什麼基於TCP的通訊程序需要進行封包和拆包.

TCP是個"流"協議,所謂流,就是沒有界限的一串數據.大家可以想想河裏的流水,是連成一片的,其間是沒有分界線的.但一般通訊程序開發是需要定義一個個相互獨立的數據包的,比如用於登陸的數據包,用於註銷的數據包.由於TCP"流"的特性以及網絡狀況,在進行數據傳輸時會出現以下幾種情況.
假設我們連續調用兩次send分別發送兩段數據data1和data2,在接收端有以下幾種接收情況(當然不止這幾種情況,這裏只列出了有代表性的情況).
A.先接收到data1,然後接收到data2.
B.先接收到data1的部分數據,然後接收到data1餘下的部分以及data2的全部.
C.先接收到了data1的全部數據和data2的部分數據,然後接收到了data2的餘下的數據.
D.一次性接收到了data1和data2的全部數據.

對於A這種情況正是我們需要的,不再做討論.對於B,C,D的情況就是大家經常說的"粘包",就需要我們把接收到的數據進行拆包,拆成一個個獨立的數據包.爲了拆包就必須在發送端進行封包.

另:對於UDP來說就不存在拆包的問題,因爲UDP是個"數據包"協議,也就是兩段數據間是有界限的,在接收端要麼接收不到數據要麼就是接收一個完整的一段數據,不會少接收也不會多接收.

二.爲什麼會出現B.C.D的情況.
"粘包"可發生在發送端也可發生在接收端.
1.由Nagle算法造成的發送端的粘包:Nagle算法是一種改善網絡傳輸效率的算法.簡單的說,當我們提交一段數據給TCP發送時,TCP並不立刻發送此段數據,而是等待一小段時間,看看在等待期間是否還有要發送的數據,若有則會一次把這兩段數據發送出去.這是對Nagle算法一個簡單的解釋,詳細的請看相關書籍.象C和D的情況就有可能是Nagle算法造成的.
2.接收端接收不及時造成的接收端粘包:TCP會把接收到的數據存在自己的緩衝區中,然後通知應用層取數據.當應用層由於某些原因不能及時的把TCP的數據取出來,就會造成TCP緩衝區中存放了幾段數據.

三.怎樣封包和拆包.
   最初遇到"粘包"的問題時,我是通過在兩次send之間調用sleep來休眠一小段時間來解決.這個解決方法的缺點是顯而易見的,使傳輸效率大大降低,而且也並不可靠.後來就是通過應答的方式來解決,儘管在大多數時候是可行的,但是不能解決象B的那種情況,而且採用應答方式增加了通訊量,加重了網絡負荷. 再後來就是對數據包進行封包和拆包的操作.
    封包:
封包就是給一段數據加上包頭,這樣一來數據包就分爲包頭和包體兩部分內容了(以後講過濾非法包時封包會加入"包尾"內容).包頭其實上是個大小固定的結構體,其中有個結構體成員變量表示包體的長度,這是個很重要的變量,其他的結構體成員可根據需要自己定義.根據包頭長度固定以及包頭中含有包體長度的變量就能正確的拆分出一個完整的數據包.
    對於拆包目前我最常用的是以下兩種方式.
    1.動態緩衝區暫存方式.之所以說緩衝區是動態的是因爲當需要緩衝的數據長度超出緩衝區的長度時會增大緩衝區長度.
    大概過程描述如下:
    A,爲每一個連接動態分配一個緩衝區,同時把此緩衝區和SOCKET關聯,常用的是通過結構體關聯.
    B,當接收到數據時首先把此段數據存放在緩衝區中.
    C,判斷緩存區中的數據長度是否夠一個包頭的長度,如不夠,則不進行拆包操作.
    D,根據包頭數據解析出裏面代表包體長度的變量.
    E,判斷緩存區中除包頭外的數據長度是否夠一個包體的長度,如不夠,則不進行拆包操作.
    F,取出整個數據包.這裏的"取"的意思是不光從緩衝區中拷貝出數據包,而且要把此數據包從緩存區中刪除掉.刪除的辦法就是把此包後面的數據移動到緩衝區的起始地址.

這種方法有兩個缺點.1.爲每個連接動態分配一個緩衝區增大了內存的使用.2.有三個地方需要拷貝數據,一個地方是把數據存放在緩衝區,一個地方是把完整的數據包從緩衝區取出來,一個地方是把數據包從緩衝區中刪除.第二種拆包的方法會解決和完善這些缺點.

前面提到過這種方法的缺點.下面給出一個改進辦法, 即採用環形緩衝.但是這種改進方法還是不能解決第一個缺點以及第一個數據拷貝,只能解決第三個地方的數據拷貝(這個地方是拷貝數據最多的地方).第2種拆包方式會解決這兩個問題.
環形緩衝實現方案是定義兩個指針,分別指向有效數據的頭和尾.在存放數據和刪除數據時只是進行頭尾指針的移動.

2.利用底層的緩衝區來進行拆包
由於TCP也維護了一個緩衝區,所以我們完全可以利用TCP的緩衝區來緩存我們的數據,這樣一來就不需要爲每一個連接分配一個緩衝區了.另一方面我們知道recv或者wsarecv都有一個參數,用來表示我們要接收多長長度的數據.利用這兩個條件我們就可以對第一種方法進行優化.
     對於阻塞SOCKET來說,我們可以利用一個循環來接收包頭長度的數據,然後解析出代表包體長度的那個變量,再用一個循環來接收包體長度的數據.
相關代碼如下:
  
char PackageHead[1024];
char PackageContext[1024*20];

int len;
PACKAGE_HEAD *pPackageHead;
while( m_bClose == false )
{
memset(PackageHead,0,sizeof(PACKAGE_HEAD));
len = m_TcpSock.ReceiveSize((char*)PackageHead,sizeof(PACKAGE_HEAD));
if( len == SOCKET_ERROR )
{
    break;
}
if(len == 0)
{
    break;
}
pPackageHead = (PACKAGE_HEAD *)PackageHead;
memset(PackageContext,0,sizeof(PackageContext));
if(pPackageHead->nDataLen>0)
{
len = m_TcpSock.ReceiveSize((char*)PackageContext,pPackageHead->nDataLen);
}
        }

m_TcpSock是一個封裝了SOCKET的類的變量,其中的ReceiveSize用於接收一定長度的數據,直到接收了一定長度的數據或者網絡出錯才返回.


int winSocket::ReceiveSize( char* strData, int iLen )
{
if( strData == NULL )
return ERR_BADPARAM;
char *p = strData;
int len = iLen;
int ret = 0;
int returnlen = 0;
while( len > 0)
{
ret = recv( m_hSocket, p+(iLen-len), iLen-returnlen, 0 );
if ( ret == SOCKET_ERROR || ret == 0 )
{

return ret;
}

len -= ret;
returnlen += ret;
}

return returnlen;
}
對於非阻塞的SOCKET,比如完成端口,我們可以提交接收包頭長度的數據的請求,當 GetQueuedCompletionStatus返回時,我們判斷接收的數據長度是否等於包頭長度,若等於,則提交接收包體長度的數據的請求,若不等於則提交接收剩餘數據的請求.當接收包體時,採用類似的方法.

載自: http://blog.csdn.net/fjcailei/archive/2009/06/17/4276463.aspx
======================================================================
幾個問題:http://www.qqgb.com/Program/VC/VCJQ/Program_200509.html
這個問題產生於編程中遇到的幾個問題: 
1、使用TCP的Socket發送數據的時候,會出現發送出錯,WSAEWOULDBLOCK,在TCP中不是會保證發送的數據能夠安全的到達接收端的嗎?也有窗口機制去防止發送速度過快,爲什麼還會出錯呢? 

2、TCP協議,在使用Socket發送數據的時候,每次發送一個包,接收端是完整的接受到一個包還是怎麼樣?如果是每發一個包,就接受一個包,爲什麼還會出現粘包問題,具體是怎麼運行的? 

3、關於Send,是不是只有在非阻塞狀態下才會出現實際發送的比指定發送的小?在阻塞狀態下會不會出現實際發送的比指定發送的小,就是說只能出現要麼全發送,要麼不發送?在非阻塞狀態下,如果之發送了一些數據,要怎麼處理,調用了Send函數後,發現返回值比指定的要小,具體要怎麼做? 

4、最後一個問題,就是TCP/IP協議和Socket是什麼關係?是指具體的實現上,Socket是TCP/IP的實現?那麼爲什麼會出現使用TCP協議的Socket會發送出錯(又回到第一個問題了,汗一個) 

實在是有點暈了,如果我的問題有不清楚的地方,或者分數有問題,歡迎指出,謝謝


這個問題第1個回答:
1 應該是你的緩衝區不夠大, 
2 tcp是流,沒有界限.也就所所謂的包. 
3 阻塞也會出現這種現象,出現後繼續發送沒發送出去的. 
4 tcp是協議,socket是一種接口,沒必然聯繫.錯誤取決於你使用接口的問題,跟tcp沒關係.


這個問題第2個回答:
1 應該是你的緩衝區不夠大, 
2 tcp是流,沒有界限.也就無所謂包. 
3 阻塞也會出現這種現象,出現後繼續發送沒發送出去的. 
4 tcp是協議,socket是一種接口,沒必然聯繫.錯誤取決於你使用接口的問題,跟tcp沒關係.


這個問題第3個回答:
1、應該不是緩衝區大小問題,我試過設置緩衝區大小,不過這裏有個問題,就是就算我把緩衝區設置成幾G,也返回成功,不過實際上怎麼可能設置那麼大、、、 

3、出現沒發送完的時候要手動發送吧,有沒有具體的代碼實現? 

4、當選擇TCP的Socket發送數據的時候,TCP中的窗口機制不是能防止發送速度過快的嗎?爲什麼Socket在出現了WSAEWOULDBLOCK後沒有處理?


這個問題第4個回答:
1.在使用非阻塞模式的情況下,如果系統發送緩衝區已滿,並示及時發送到對端,就會產生該錯誤,繼續重試即可。 
3.如果沒有發完就繼續發送後續部分即可。


這個問題第5個回答:
1、使用非阻塞模式時,如果當前操作不能立即完成則會返回失敗,錯誤碼是WSAEWOULDBLOCK,這是正常的,程序可以先執行其它任務,過一段時間後再重試該操作。 
2、發送與接收不是一一對應的,TCP會把各次發送的數據重新組合,可能合併也可能拆分,但發送次序是不變的。 
3、在各種情況下都要根據send的返回值來確定發送了多少數據,沒有發送完就再接着發。 
4、socket是Windows提供網絡編程接口,TCP/IP是網絡傳輸協議,使用socket是可以使用多種協議,其中包括TCP/IP。


這個問題第6個回答:
up


這個問題第7個回答:
發送的過程是:發送到緩衝區和從緩衝區發送到網絡上 
WSAEWOULDBLOCK和粘包都是出現在發送到緩衝區這個過程的

無邊界的流解析實際上就是靠用戶(開發者)自行解決邊界問題

實際應用中確實會用數據頭格式,類似HTTP協議模式,例如協議版本信息還有數據包大小都會放在接收頭部,數據內容放在最末端。不會像博文裏面【數據大小 + 數據內容】這麼簡單的模式,萬變不離其宗,實際應用中只是把數據大小的信息擴展了一下,變成【數據頭部信息 + 數據內容】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章