socket的半包,粘包與分包的問題

http://zhaohuiopensource.iteye.com/blog/1541270

首先看兩個(gè)概念:

短連接:

連接->傳輸數(shù)據(jù)->關(guān)閉連接

HTTP是無狀態(tài)的,瀏覽器和服務(wù)器每進(jìn)行一次HTTP操作,就建立一次連接,但任務(wù)結(jié)束就中斷連接。

也可以這樣說:短連接是指SOCKET連接后發(fā)送后接收完數(shù)據(jù)后馬上斷開連接。

長連接:

連接->傳輸數(shù)據(jù)->保持連接 -> 傳輸數(shù)據(jù)-> 。。。 ->關(guān)閉連接。

長連接指建立SOCKET連接后不管是否使用都保持連接,但安全性較差。

之所以出現(xiàn)粘包和半包現(xiàn)象,是因?yàn)門CP當(dāng)中,只有流的概念,沒有包的概念.

半包

指接受方?jīng)]有接受到一個(gè)完整的包,只接受了部分,這種情況主要是由于TCP為提高傳輸效率,將一個(gè)包分配的足夠大,導(dǎo)致接受方并不能一次接受完。(在長連接和短連接中都會(huì)出現(xiàn))。

粘包與分包

指發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時(shí)粘成一包,從接收緩沖區(qū)看,后一包數(shù)據(jù)的頭緊接著前一包數(shù)據(jù)的尾。出現(xiàn)粘包現(xiàn)象的原因是多方面的,它既可能由發(fā)送方造成,也可能由接收方造成。發(fā)送方引起的粘包是由TCP協(xié)議本身造成的,TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一包數(shù)據(jù)。若連續(xù)幾次發(fā)送的數(shù)據(jù)都很少,通常TCP會(huì)根據(jù)優(yōu)化算法把這些數(shù)據(jù)合成一包后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)。接收方引起的粘包是由于接收方用戶進(jìn)程不及時(shí)接收數(shù)據(jù),從而導(dǎo)致粘包現(xiàn)象。這是因?yàn)榻邮辗较劝咽盏降臄?shù)據(jù)放在系統(tǒng)接收緩沖區(qū),用戶進(jìn)程從該緩沖區(qū)取數(shù)據(jù),若下一包數(shù)據(jù)到達(dá)時(shí)前一包數(shù)據(jù)尚未被用戶進(jìn)程取走,則下一包數(shù)據(jù)放到系統(tǒng)接收緩沖區(qū)時(shí)就接到前一包數(shù)據(jù)之后,而用戶進(jìn)程根據(jù)預(yù)先設(shè)定的緩沖區(qū)大小從系統(tǒng)接收緩沖區(qū)取數(shù)據(jù),這樣就一次取到了多包數(shù)據(jù)。分包是指在出現(xiàn)粘包的時(shí)候我們的接收方要進(jìn)行分包處理。(在長連接中都會(huì)出現(xiàn))

什么時(shí)候需要考慮半包的情況?

從備注中我們了解到Socket內(nèi)部默認(rèn)的收發(fā)緩沖區(qū)大小大概是8K,但是我們?cè)趯?shí)際中往往需要考慮效率問題,重新配置了這個(gè)值,來達(dá)到系統(tǒng)的最佳狀態(tài)。

一個(gè)實(shí)際中的例子:用mina作為服務(wù)器端,使用的緩存大小為10k,這里使用的是短連接,所有不用考慮粘包的問題。

問題描述:在并發(fā)量比較大的情況下,就會(huì)出現(xiàn)一次接受并不能完整的獲取所有的數(shù)據(jù)。

處理方式:

1.通過包頭+包長+包體的協(xié)議形式,當(dāng)服務(wù)器端獲取到指定的包長時(shí)才說明獲取完整。

2.指定包的結(jié)束標(biāo)識(shí),這樣當(dāng)我們獲取到指定的標(biāo)識(shí)時(shí),說明包獲取完整。

什么時(shí)候需要考慮粘包的情況?

1.當(dāng)時(shí)短連接的情況下,不用考慮粘包的情況

2.如果發(fā)送數(shù)據(jù)無結(jié)構(gòu),如文件傳輸,這樣發(fā)送方只管發(fā)送,接收方只管接收存儲(chǔ)就ok,也不用考慮粘包

3.如果雙方建立連接,需要在連接后一段時(shí)間內(nèi)發(fā)送不同結(jié)構(gòu)數(shù)據(jù)

處理方式:

接收方創(chuàng)建一預(yù)處理線程,對(duì)接收到的數(shù)據(jù)包進(jìn)行預(yù)處理,將粘連的包分開

注:粘包情況有兩種,一種是粘在一起的包都是完整的數(shù)據(jù)包,另一種情況是粘在一起的包有不完整的包

備注:

一個(gè)包沒有固定長度,以太網(wǎng)限制在46-1500字節(jié),1500就是以太網(wǎng)的MTU,超過這個(gè)量,TCP會(huì)為IP數(shù)據(jù)報(bào)設(shè)置偏移量進(jìn)行分片傳輸,現(xiàn)在一般可允許應(yīng)用層設(shè)置8k(NTFS系)的緩沖區(qū),8k的數(shù)據(jù)由底層分片,而應(yīng)用看來只是一次發(fā)送。windows的緩沖區(qū)經(jīng)驗(yàn)值是4k,Socket本身分為兩種,流(TCP)和數(shù)據(jù)報(bào)(UDP),你的問題針對(duì)這兩種不同使用而結(jié)論不一樣。甚至還和你是用阻塞、還是非阻塞Socket來編程有關(guān)。

1、通信長度,這個(gè)是你自己決定的,沒有系統(tǒng)強(qiáng)迫你要發(fā)多大的包,實(shí)際應(yīng)該根據(jù)需求和網(wǎng)絡(luò)狀況來決定。對(duì)于TCP,這個(gè)長度可以大點(diǎn),但要知道,Socket內(nèi)部默認(rèn)的收發(fā)緩沖區(qū)大小大概是8K,你可以用SetSockOpt來改變。但對(duì)于UDP,就不要太大,一般在1024至10K。注意一點(diǎn),你無論發(fā)多大的包,IP層和鏈路層都會(huì)把你的包進(jìn)行分片發(fā)送,一般局域網(wǎng)就是1500左右,廣域網(wǎng)就只有幾十字節(jié)。分片后的包將經(jīng)過不同的路由到達(dá)接收方,對(duì)于UDP而言,要是其中一個(gè)分片丟失,那么接收方的IP層將把整個(gè)發(fā)送包丟棄,這就形成丟包。顯然,要是一個(gè)UDP發(fā)包佷大,它被分片后,鏈路層丟失分片的幾率就佷大,你這個(gè)UDP包,就佷容易丟失,但是太小又影響效率。最好可以配置這個(gè)值,以根據(jù)不同的環(huán)境來調(diào)整到最佳狀態(tài)。

send()函數(shù)返回了實(shí)際發(fā)送的長度,在網(wǎng)絡(luò)不斷的情況下,它絕不會(huì)返回(發(fā)送失敗的)錯(cuò)誤,最多就是返回0。對(duì)于TCP你可以字節(jié)寫一個(gè)循環(huán)發(fā)送。當(dāng)send函數(shù)返回SOCKET_ERROR時(shí),才標(biāo)志著有錯(cuò)誤。但對(duì)于UDP,你不要寫循環(huán)發(fā)送,否則將給你的接收帶來極大的麻煩。所以UDP需要用SetSockOpt來改變Socket內(nèi)部Buffer的大小,以能容納你的發(fā)包。明確一點(diǎn),TCP作為流,發(fā)包是不會(huì)整包到達(dá)的,而是源源不斷的到,那接收方就必須組包。而UDP作為消息或數(shù)據(jù)報(bào),它一定是整包到達(dá)接收方。

2、關(guān)于接收,一般的發(fā)包都有包邊界,首要的就是你這個(gè)包的長度要讓接收方知道,于是就有個(gè)包頭信息,對(duì)于TCP,接收方先收這個(gè)包頭信息,然后再收包數(shù)據(jù)。一次收齊整個(gè)包也可以,可要對(duì)結(jié)果是否收齊進(jìn)行驗(yàn)證。這也就完成了組包過程。UDP,那你只能整包接收了。要是你提供的接收Buffer過小,TCP將返回實(shí)際接收的長度,余下的還可以收,而UDP不同的是,余下的數(shù)據(jù)被丟棄并返回WSAEMSGSIZE錯(cuò)誤。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多個(gè)發(fā)包,你必須分離它們,還有就是當(dāng)Buffer太小,而一次收不完Socket內(nèi)部的數(shù)據(jù),那么Socket接收事件(OnReceive),可能不會(huì)再觸發(fā),使用事件方式進(jìn)行接收時(shí),密切注意這點(diǎn)。這些特性就是體現(xiàn)了流和數(shù)據(jù)包的區(qū)別。

參照:http://176170847.iteye.com/blog/819446

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容