日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TCP粘包问题分析和解决(全)

發(fā)布時(shí)間:2023/11/30 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TCP粘包问题分析和解决(全) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

TCP通信粘包問題分析和解決(全)

在socket網(wǎng)絡(luò)程序中,TCP和UDP分別是面向連接和非面向連接的。因此TCP的socket編程,收發(fā)兩端(客戶端和服務(wù)器端)都要有成對(duì)的socket,因此,發(fā)送端為了將多個(gè)發(fā)往接收端的包,更有效的發(fā)到對(duì)方,使用了優(yōu)化方法(Nagle算法),將多次間隔較小、數(shù)據(jù)量小的數(shù)據(jù),合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包。這樣,接收端,就難于分辨出來了,必須提供科學(xué)的拆包機(jī)制。

對(duì)于UDP,不會(huì)使用塊的合并優(yōu)化算法,這樣,實(shí)際上目前認(rèn)為,是由于UDP支持的是一對(duì)多的模式,所以接收端的skbuff(套接字緩沖區(qū))采用了鏈?zhǔn)浇Y(jié)構(gòu)來記錄每一個(gè)到達(dá)的UDP包,在每個(gè)UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對(duì)于接收端來說,就容易進(jìn)行區(qū)分處理了。所以UDP不會(huì)出現(xiàn)粘包問題。

====================================================================

在介紹TCP之前先普及下兩個(gè)相關(guān)的概念,長(zhǎng)連接和短連接。

1.長(zhǎng)連接

Client方與Server方先建立通訊連接,連接建立后 不斷開, 然后再進(jìn)行報(bào)文發(fā)送和接收。

2.短連接

Client方與Server每進(jìn)行一次報(bào)文收發(fā)交易時(shí)才進(jìn)行通訊連接,交易完畢后立即斷開連接。此種方式常用于一點(diǎn)對(duì)多點(diǎn)通訊,比如多個(gè)Client連接一個(gè)Server.

?

?

TCP協(xié)議簡(jiǎn)介

TCP是一個(gè)面向連接的傳輸層協(xié)議,雖然TCP不屬于ISO制定的協(xié)議集,但由于其在商業(yè)界和工業(yè)界的成功應(yīng)用,它已成為事實(shí)上的網(wǎng)絡(luò)標(biāo)準(zhǔn),廣泛應(yīng)用于各種網(wǎng)絡(luò)主機(jī)間的通信。

作為一個(gè)面向連接的傳輸層協(xié)議,TCP的目標(biāo)是為用戶提供可靠的端到端連接,保證信息有序無誤的傳輸。它除了提供基本的數(shù)據(jù)傳輸功能外,還為保證可靠性采用了數(shù)據(jù)編號(hào)、校驗(yàn)和計(jì)算、數(shù)據(jù)確認(rèn)等一系列措施。它對(duì)傳送的每個(gè)數(shù)據(jù)字節(jié)都進(jìn)行編號(hào),并請(qǐng)求接收方回傳確認(rèn)信息(ACK)。發(fā)送方如果在規(guī)定的時(shí)間內(nèi)沒有收到數(shù)據(jù)確認(rèn),就重傳該數(shù)據(jù)。

(1)?????數(shù)據(jù)編號(hào)使接收方能夠處理數(shù)據(jù)的失序和重復(fù)問題。

(2)?????數(shù)據(jù)誤碼問題通過在每個(gè)傳輸?shù)臄?shù)據(jù)段中增加校驗(yàn)和予以解決,接收方在接收到數(shù)據(jù)后檢查校驗(yàn)和,若校驗(yàn)和有誤,則丟棄該有誤碼的數(shù)據(jù)段,并要求發(fā)送方重傳。

(3)?????流量控制也是保證可靠性的一個(gè)重要措施,若無流控,可能會(huì)因接收緩沖區(qū)溢出而丟失大量數(shù)據(jù),導(dǎo)致許多重傳,造成網(wǎng)絡(luò)擁塞惡性循環(huán)。

(4)?????TCP采用可變窗口進(jìn)行流量控制,由接收方控制發(fā)送方發(fā)送的數(shù)據(jù)量。

TCP為用戶提供了高可靠性的網(wǎng)絡(luò)傳輸服務(wù),但可靠性保障措施也影響了傳輸效率。因此,在實(shí)際工程應(yīng)用中,只有關(guān)鍵數(shù)據(jù)的傳輸才采用TCP,而普通數(shù)據(jù)的傳輸一般采用高效率的UDP。

?

保護(hù)消息邊界和流

那么什么是保護(hù)消息邊界和流呢?

保護(hù)消息邊界,就是指?jìng)鬏攨f(xié)議把數(shù)據(jù)當(dāng)作一條獨(dú)立的消息在網(wǎng)上傳輸,接收端只能接收獨(dú)立的消息。也就是說存在保護(hù)消息邊界,接收端一次只能接收發(fā)送端發(fā)出的一個(gè)數(shù)據(jù)包。而面向流則是指無保護(hù)消息保護(hù)邊界的,如果發(fā)送端連續(xù)發(fā)送數(shù)據(jù),接收端有可能在一次接收動(dòng)作中,會(huì)接收兩個(gè)或者更多的數(shù)據(jù)包。

例如,我們連續(xù)發(fā)送三個(gè)數(shù)據(jù)包,大小分別是2k,4k ,8k,這三個(gè)數(shù)據(jù)包,都已經(jīng)到達(dá)了接收端的網(wǎng)絡(luò)堆棧中,如果使用UDP協(xié)議,不管我們使用多大的接收緩沖區(qū)去接收數(shù)據(jù),我們必須有三次接收動(dòng)作,才能夠把所有的數(shù)據(jù)包接收完.而使用TCP協(xié)議,我們只要把接收的緩沖區(qū)大小設(shè)置在14k以上,我們就能夠一次把所有的數(shù)據(jù)包接收下來,只需要有一次接收動(dòng)作。

?

注意:

這就是因?yàn)?span style="margin:0px;padding:0px;">UDP協(xié)議的保護(hù)消息邊界使得每一個(gè)消息都是獨(dú)立的。而流傳輸卻把數(shù)據(jù)當(dāng)作一串?dāng)?shù)據(jù)流,他不認(rèn)為數(shù)據(jù)是一個(gè)一個(gè)的消息。所以有很多人在使用tcp協(xié)議通訊的時(shí)候,并不清楚tcp是基于流的傳輸,當(dāng)連續(xù)發(fā)送數(shù)據(jù)的時(shí)候,他們時(shí)常會(huì)認(rèn)識(shí)tcp會(huì)丟包。其實(shí)不然,因?yàn)楫?dāng)他們使用的緩沖區(qū)足夠大時(shí),他們有可能會(huì)一次接收到兩個(gè)甚至更多的數(shù)據(jù)包,而很多人往往會(huì)忽視這一點(diǎn),只解析檢查了第一個(gè)數(shù)據(jù)包,而已經(jīng)接收的其他數(shù)據(jù)包卻被忽略了。所以大家如果要作這類的網(wǎng)絡(luò)編程的時(shí)候,必須要注意這一點(diǎn)。

?

結(jié)論:

(1)TCP為了保證可靠傳輸,盡量減少額外開銷(每次發(fā)包都要驗(yàn)證),因此采用了流式傳輸,面向流的傳輸,相對(duì)于面向消息的傳輸,可以減少發(fā)送包的數(shù)量,從而減少了額外開銷。但是,對(duì)于數(shù)據(jù)傳輸頻繁的程序來講,使用TCP可能會(huì)容易粘包。當(dāng)然,對(duì)接收端的程序來講,如果機(jī)器負(fù)荷很重,也會(huì)在接收緩沖里粘包。這樣,就需要接收端額外拆包,增加了工作量。因此,這個(gè)特別適合的是數(shù)據(jù)要求可靠傳輸,但是不需要太頻繁傳輸?shù)膱?chǎng)合(兩次操作間隔100ms,具體是由TCP等待發(fā)送間隔決定的,取決于內(nèi)核中的socket的寫法)

(2)UDP,由于面向的是消息傳輸,它把所有接收到的消息都掛接到緩沖區(qū)的接受隊(duì)列中,因此,它對(duì)于數(shù)據(jù)的提取分離就更加方便,但是,它沒有粘包機(jī)制,因此,當(dāng)發(fā)送數(shù)據(jù)量較小的時(shí)候,就會(huì)發(fā)生數(shù)據(jù)包有效載荷較小的情況,也會(huì)增加多次發(fā)送的系統(tǒng)發(fā)送開銷(系統(tǒng)調(diào)用,寫硬件等)和接收開銷。因此,應(yīng)該最好設(shè)置一個(gè)比較合適的數(shù)據(jù)包的包長(zhǎng),來進(jìn)行UDP數(shù)據(jù)的發(fā)送。(UDP最大載荷為1472,因此最好能每次傳輸接近這個(gè)數(shù)的數(shù)據(jù)量,這特別適合于視頻,音頻等大塊數(shù)據(jù)的發(fā)送,同時(shí),通過減少握手來保證流媒體的實(shí)時(shí)性

====================================================================

粘包問題分析與對(duì)策

TCP粘包是指發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時(shí)粘成一包,從接收緩沖區(qū)看,后一包數(shù)據(jù)的頭緊接著前一包數(shù)據(jù)的尾。

出現(xiàn)粘包現(xiàn)象的原因是多方面的,它既可能由發(fā)送方造成,也可能由接收方造成。

?

什么時(shí)候需要考慮粘包問題

1如果利用tcp每次發(fā)送數(shù)據(jù),就與對(duì)方建立連接,然后雙方發(fā)送完一段數(shù)據(jù)后,就關(guān)閉連接,這樣就不會(huì)出現(xiàn)粘包問題(因?yàn)橹挥幸环N包結(jié)構(gòu),類似于http協(xié)議)。

關(guān)閉連接主要是要雙方都發(fā)送close連接(參考tcp關(guān)閉協(xié)議)。如:A需要發(fā)送一段字符串給B,那么A與B建立連接,然后發(fā)送雙方都默認(rèn)好的協(xié)議字符如"hello give me sth abour yourself",然后B收到報(bào)文后,就將緩沖區(qū)數(shù)據(jù)接收,然后關(guān)閉連接,這樣粘包問題不用考慮到,因?yàn)榇蠹叶贾朗前l(fā)送一段字符。

2如果發(fā)送數(shù)據(jù)無結(jié)構(gòu),如文件傳輸,這樣發(fā)送方只管發(fā)送,接收方只管接收存儲(chǔ)就ok,也不用考慮粘包3如果雙方建立連接,需要在連接后一段時(shí)間內(nèi)發(fā)送不同結(jié)構(gòu)數(shù)據(jù),如連接后,有好幾種結(jié)構(gòu):

1)"hellogive me sth abour yourself"

2)"Don'tgive me sth abour yourself"

那這樣的話,如果發(fā)送方連續(xù)發(fā)送這個(gè)兩個(gè)包出去,接收方一次接收可能會(huì)是"hellogive me sth abour yourselfDon't give me sth abour yourself"這樣接收方就傻了,到底是要干嘛?不知道,因?yàn)閰f(xié)議沒有規(guī)定這么詭異的字符串,所以要處理把它分包,怎么分也需要雙方組織一個(gè)比較好的包結(jié)構(gòu),所以一般可能會(huì)在頭加一個(gè)數(shù)據(jù)長(zhǎng)度之類的包,以確保接收。

?

粘包出現(xiàn)原因

簡(jiǎn)單得說,在流傳輸中出現(xiàn),UDP不會(huì)出現(xiàn)粘包,因?yàn)樗?span style="margin:0px;padding:0px;">消息邊界(參考Windows網(wǎng)絡(luò)編程)

1發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包

2接收方不及時(shí)接收緩沖區(qū)的包,造成多個(gè)包接收

具體點(diǎn):

(1)發(fā)送方引起的粘包是由TCP協(xié)議本身造成的,TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一包數(shù)據(jù)。若連續(xù)幾次發(fā)送的數(shù)據(jù)都很少,通常TCP會(huì)根據(jù)優(yōu)化算法把這些數(shù)據(jù)合成一包后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)。

(2)接收方引起的粘包是由于接收方用戶進(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ù)。

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

不是所有的粘包現(xiàn)象都需要處理,若傳輸?shù)臄?shù)據(jù)為不帶結(jié)構(gòu)的連續(xù)流數(shù)據(jù)(如文件傳輸),則不必把粘連的包分開(簡(jiǎn)稱分包)。但在實(shí)際工程應(yīng)用中,傳輸?shù)臄?shù)據(jù)一般為帶結(jié)構(gòu)的數(shù)據(jù),這時(shí)就需要做分包處理。

在處理定長(zhǎng)結(jié)構(gòu)數(shù)據(jù)的粘包問題時(shí),分包算法比較簡(jiǎn)單;在處理不定長(zhǎng)結(jié)構(gòu)數(shù)據(jù)的粘包問題時(shí),分包算法就比較復(fù)雜。特別是粘在一起的包有不完整的包的粘包情況,由于一包數(shù)據(jù)內(nèi)容被分在了兩個(gè)連續(xù)的接收包中,處理起來難度較大。實(shí)際工程應(yīng)用中應(yīng)盡量避免出現(xiàn)粘包現(xiàn)象。

?

為了避免粘包現(xiàn)象,可采取以下幾種措施:

(1)對(duì)于發(fā)送方引起的粘包現(xiàn)象,用戶可通過編程設(shè)置來避免,TCP提供了強(qiáng)制數(shù)據(jù)立即傳送的操作指令push,TCP軟件收到該操作指令后,就立即將本段數(shù)據(jù)發(fā)送出去,而不必等待發(fā)送緩沖區(qū)滿;

(2)對(duì)于接收方引起的粘包,則可通過優(yōu)化程序設(shè)計(jì)、精簡(jiǎn)接收進(jìn)程工作量、提高接收進(jìn)程優(yōu)先級(jí)等措施,使其及時(shí)接收數(shù)據(jù),從而盡量避免出現(xiàn)粘包現(xiàn)象;

(3)由接收方控制,將一包數(shù)據(jù)按結(jié)構(gòu)字段,人為控制分多次接收,然后合并,通過這種手段來避免粘包。

?

以上提到的三種措施,都有其不足之處。

(1)第一種編程設(shè)置方法雖然可以避免發(fā)送方引起的粘包,但它關(guān)閉了優(yōu)化算法,降低了網(wǎng)絡(luò)發(fā)送效率,影響應(yīng)用程序的性能,一般不建議使用。

(2)第二種方法只能減少出現(xiàn)粘包的可能性,但并不能完全避免粘包,當(dāng)發(fā)送頻率較高時(shí),或由于網(wǎng)絡(luò)突發(fā)可能使某個(gè)時(shí)間段數(shù)據(jù)包到達(dá)接收方較快,接收方還是有可能來不及接收,從而導(dǎo)致粘包。

(3)第三種方法雖然避免了粘包,但應(yīng)用程序的效率較低,對(duì)實(shí)時(shí)應(yīng)用的場(chǎng)合不適合。

?

一種比較周全的對(duì)策是:接收方創(chuàng)建一預(yù)處理線程,對(duì)接收到的數(shù)據(jù)包進(jìn)行預(yù)處理,將粘連的包分開。對(duì)這種方法我們進(jìn)行了實(shí)驗(yàn),證明是高效可行的。

具體可以參考:http://blog.csdn.net/soli/article/details/1297109

?

TCP無保護(hù)消息邊界的解決

針對(duì)這個(gè)問題,一般有3種解決方案:

(1)發(fā)送固定長(zhǎng)度的消息

(2)把消息的尺寸與消息一塊發(fā)送

(3)使用特殊標(biāo)記來區(qū)分消息間隔

其解決方法具體解決可以參考:http://blog.csdn.net/zhangxinrun/article/details/6721427

?

====================================================================

網(wǎng)絡(luò)通訊的封包和拆包

對(duì)于基于TCP開發(fā)的通訊程序,有個(gè)很重要的問題需要解決,就是封包和拆包。

?

為什么基于TCP的通訊程序需要進(jìn)行封包和拆包

TCP是個(gè)"流"協(xié)議,所謂流,就是沒有界限的一串?dāng)?shù)據(jù),大家可以想想河里的流水,是連成一片的,其間是沒有分界線的。但一般通訊程序開發(fā)是需要定義一個(gè)個(gè)相互獨(dú)立的數(shù)據(jù)包的,比如用于登陸的數(shù)據(jù)包,用于注銷的數(shù)據(jù)包。由于TCP"流"的特性以及網(wǎng)絡(luò)狀況,在進(jìn)行數(shù)據(jù)傳輸時(shí)會(huì)出現(xiàn)以下幾種情況。

假設(shè)我們連續(xù)調(diào)用兩次send分別發(fā)送兩段數(shù)據(jù)data1和data2,在接收端有以下幾種接收情況(當(dāng)然不止這幾種情況,這里只列出了有代表性的情況).

A.先接收到data1,然后接收到data2.

B.先接收到data1的部分?jǐn)?shù)據(jù),然后接收到data1余下的部分以及data2的全部.

C.先接收到了data1的全部數(shù)據(jù)和data2的部分?jǐn)?shù)據(jù),然后接收到了data2的余下的數(shù)據(jù).

D.一次性接收到了data1和data2的全部數(shù)據(jù).

對(duì)于A這種情況正是我們需要的,不再做討論.對(duì)于B,C,D的情況就是大家經(jīng)常說的"粘包",就需要我們把接收到的數(shù)據(jù)進(jìn)行拆包,拆成一個(gè)個(gè)獨(dú)立的數(shù)據(jù)包,為了拆包就必須在發(fā)送端進(jìn)行封包。

另:對(duì)于UDP來說就不存在拆包的問題,因?yàn)閁DP是個(gè)"數(shù)據(jù)包"協(xié)議,也就是兩段數(shù)據(jù)間是有界限的,在接收端要么接收不到數(shù)據(jù)要么就是接收一個(gè)完整的一段數(shù)據(jù),不會(huì)少接收也不會(huì)多接收。

?

為什么會(huì)出現(xiàn)B.C.D的情況

1.由Nagle算法造成的發(fā)送端的粘包:Nagle算法是一種改善網(wǎng)絡(luò)傳輸效率的算法.簡(jiǎn)單的說,當(dāng)我們提交一段數(shù)據(jù)給TCP發(fā)送時(shí),TCP并不立刻發(fā)送此段數(shù)據(jù),而是等待一小段時(shí)間,看看在等待期間是否還有要發(fā)送的數(shù)據(jù),若有則會(huì)一次把這兩段數(shù)據(jù)發(fā)送出去.這是對(duì)Nagle算法一個(gè)簡(jiǎn)單的解釋,詳細(xì)的請(qǐng)看相關(guān)書籍. C和D的情況就有可能是Nagle算法造成的.

2.接收端接收不及時(shí)造成的接收端粘包:TCP會(huì)把接收到的數(shù)據(jù)存在自己的緩沖區(qū)中,然后通知應(yīng)用層取數(shù)據(jù).當(dāng)應(yīng)用層由于某些原因不能及時(shí)的把TCP的數(shù)據(jù)取出來,就會(huì)造成TCP緩沖區(qū)中存放了幾段數(shù)據(jù).

?

怎樣封包和拆包

最初遇到"粘包"的問題時(shí),我是通過在兩次send之間調(diào)用sleep來休眠一小段時(shí)間來解決。這個(gè)解決方法的缺點(diǎn)是顯而易見的,使傳輸效率大大降低,而且也并不可靠。后來就是通過應(yīng)答的方式來解決,盡管在大多數(shù)時(shí)候是可行的,但是不能解決B的那種情況,而且采用應(yīng)答方式增加了通訊量,加重了網(wǎng)絡(luò)負(fù)荷. 再后來就是對(duì)數(shù)據(jù)包進(jìn)行封包和拆包的操作。

?

封包

封包就是給一段數(shù)據(jù)加上包頭,這樣一來數(shù)據(jù)包就分為包頭和包體兩部分內(nèi)容了(以后講過濾非法包時(shí)封包會(huì)加入"包尾"內(nèi)容)。包頭其實(shí)上是個(gè)大小固定的結(jié)構(gòu)體,其中有個(gè)結(jié)構(gòu)體成員變量表示包體的長(zhǎng)度,這是個(gè)很重要的變量,其他的結(jié)構(gòu)體成員可根據(jù)需要自己定義。根據(jù)包頭長(zhǎng)度固定以及包頭中含有包體長(zhǎng)度的變量就能正確的拆分出一個(gè)完整的數(shù)據(jù)包。

?

拆包

對(duì)于拆包目前我最常用的是以下兩種方式:

(1)動(dòng)態(tài)緩沖區(qū)暫存方式。之所以說緩沖區(qū)是動(dòng)態(tài)的是因?yàn)楫?dāng)需要緩沖的數(shù)據(jù)長(zhǎng)度超出緩沖區(qū)的長(zhǎng)度時(shí)會(huì)增大緩沖區(qū)長(zhǎng)度。

大概過程描述如下:

A,為每一個(gè)連接動(dòng)態(tài)分配一個(gè)緩沖區(qū),同時(shí)把此緩沖區(qū)和SOCKET關(guān)聯(lián),常用的是通過結(jié)構(gòu)體關(guān)聯(lián).

B,當(dāng)接收到數(shù)據(jù)時(shí)首先把此段數(shù)據(jù)存放在緩沖區(qū)中.

C,判斷緩存區(qū)中的數(shù)據(jù)長(zhǎng)度是否夠一個(gè)包頭的長(zhǎng)度,如不夠,則不進(jìn)行拆包操作.

D,根據(jù)包頭數(shù)據(jù)解析出里面代表包體長(zhǎng)度的變量.

E,判斷緩存區(qū)中除包頭外的數(shù)據(jù)長(zhǎng)度是否夠一個(gè)包體的長(zhǎng)度,如不夠,則不進(jìn)行拆包操作.

F,取出整個(gè)數(shù)據(jù)包.這里的"取"的意思是不光從緩沖區(qū)中拷貝出數(shù)據(jù)包,而且要把此數(shù)據(jù)包從緩存區(qū)中刪除掉.刪除的辦法就是把此包后面的數(shù)據(jù)移動(dòng)到緩沖區(qū)的起始地址.

?

這種方法有兩個(gè)缺點(diǎn).

1)?為每個(gè)連接動(dòng)態(tài)分配一個(gè)緩沖區(qū)增大了內(nèi)存的使用.

2)?有三個(gè)地方需要拷貝數(shù)據(jù),一個(gè)地方是把數(shù)據(jù)存放在緩沖區(qū),一個(gè)地方是把完整的數(shù)據(jù)包從緩沖區(qū)取出來,一個(gè)地方是把數(shù)據(jù)包從緩沖區(qū)中刪除.第二種拆包的方法會(huì)解決和完善這些缺點(diǎn).

前面提到過這種方法的缺點(diǎn).下面給出一個(gè)改進(jìn)辦法, 即采用環(huán)形緩沖.但是這種改進(jìn)方法還是不能解決第一個(gè)缺點(diǎn)以及第一個(gè)數(shù)據(jù)拷貝,只能解決第三個(gè)地方的數(shù)據(jù)拷貝(這個(gè)地方是拷貝數(shù)據(jù)最多的地方).第2種拆包方式會(huì)解決這兩個(gè)問題.

環(huán)形緩沖實(shí)現(xiàn)方案是定義兩個(gè)指針,分別指向有效數(shù)據(jù)的頭和尾.在存放數(shù)據(jù)和刪除數(shù)據(jù)時(shí)只是進(jìn)行頭尾指針的移動(dòng).

?

(2)利用底層的緩沖區(qū)來進(jìn)行拆包

由于TCP也維護(hù)了一個(gè)緩沖區(qū),所以我們完全可以利用TCP的緩沖區(qū)來緩存我們的數(shù)據(jù),這樣一來就不需要為每一個(gè)連接分配一個(gè)緩沖區(qū)了。另一方面我們知道recv或者wsarecv都有一個(gè)參數(shù),用來表示我們要接收多長(zhǎng)長(zhǎng)度的數(shù)據(jù)。利用這兩個(gè)條件我們就可以對(duì)第一種方法進(jìn)行優(yōu)化。

對(duì)于阻塞SOCKET來說,我們可以利用一個(gè)循環(huán)來接收包頭長(zhǎng)度的數(shù)據(jù),然后解析出代表包體長(zhǎng)度的那個(gè)變量,再用一個(gè)循環(huán)來接收包體長(zhǎng)度的數(shù)據(jù)。

編程實(shí)現(xiàn)見:http://blog.csdn.net/zhangxinrun/article/details/6721495


這個(gè)問題產(chǎn)生于編程中遇到的幾個(gè)問題:

1、使用TCP的Socket發(fā)送數(shù)據(jù)的時(shí)候,會(huì)出現(xiàn)發(fā)送出錯(cuò),WSAEWOULDBLOCK,在TCP中不是會(huì)保證發(fā)送的數(shù)據(jù)能夠安全的到達(dá)接收端的嗎?也有窗口機(jī)制去防止發(fā)送速度過快,為什么還會(huì)出錯(cuò)呢?

2、TCP協(xié)議,在使用Socket發(fā)送數(shù)據(jù)的時(shí)候,每次發(fā)送一個(gè)包,接收端是完整的接受到一個(gè)包還是怎么樣?如果是每發(fā)一個(gè)包,就接受一個(gè)包,為什么還會(huì)出現(xiàn)粘包問題,具體是怎么運(yùn)行的?

3、關(guān)于Send,是不是只有在非阻塞狀態(tài)下才會(huì)出現(xiàn)實(shí)際發(fā)送的比指定發(fā)送的小?在阻塞狀態(tài)下會(huì)不會(huì)出現(xiàn)實(shí)際發(fā)送的比指定發(fā)送的小,就是說只能出現(xiàn)要么全發(fā)送,要么不發(fā)送?在非阻塞狀態(tài)下,如果之發(fā)送了一些數(shù)據(jù),要怎么處理,調(diào)用了Send函數(shù)后,發(fā)現(xiàn)返回值比指定的要小,具體要怎么做?

4、最后一個(gè)問題,就是TCP/IP協(xié)議和Socket是什么關(guān)系?是指具體的實(shí)現(xiàn)上,Socket是TCP/IP的實(shí)現(xiàn)?那么為什么會(huì)出現(xiàn)使用TCP協(xié)議的Socket會(huì)發(fā)送出錯(cuò)。


這個(gè)問題第1個(gè)回答:

1應(yīng)該是你的緩沖區(qū)不夠大,

2 tcp是流,沒有界限.也就沒所謂的包.

3阻塞也會(huì)出現(xiàn)這種現(xiàn)象,出現(xiàn)后繼續(xù)發(fā)送沒發(fā)送出去的.

4tcp是協(xié)議,socket是一種接口,沒必然聯(lián)系.錯(cuò)誤取決于你使用接口的問題,跟tcp沒關(guān)系.


這個(gè)問題第2個(gè)回答:

1、應(yīng)該不是緩沖區(qū)大小問題,我試過設(shè)置緩沖區(qū)大小,不過這里有個(gè)問題,就是就算我把緩沖區(qū)設(shè)置成幾G,也返回成功,不過實(shí)際上怎么可能設(shè)置那么大

3、出現(xiàn)沒發(fā)送完的時(shí)候要手動(dòng)發(fā)送吧,有沒有具體的代碼實(shí)現(xiàn)?

4、當(dāng)選擇TCP的Socket發(fā)送數(shù)據(jù)的時(shí)候,TCP中的窗口機(jī)制不是能防止發(fā)送速度過快的嗎?為什么Socket在出現(xiàn)了WSAEWOULDBLOCK后沒有處理?


這個(gè)問題第3個(gè)回答:

1.在使用非阻塞模式的情況下,如果系統(tǒng)發(fā)送緩沖區(qū)已滿,并示及時(shí)發(fā)送到對(duì)端,就會(huì)產(chǎn)生該錯(cuò)誤,繼續(xù)重試即可。

3.如果沒有發(fā)完就繼續(xù)發(fā)送后續(xù)部分即可。


這個(gè)問題第4個(gè)回答:

1、使用非阻塞模式時(shí),如果當(dāng)前操作不能立即完成則會(huì)返回失敗,錯(cuò)誤碼是WSAEWOULDBLOCK,這是正常的,程序可以先執(zhí)行其它任務(wù),過一段時(shí)間后再重試該操作。

2、發(fā)送與接收不是一一對(duì)應(yīng)的,TCP會(huì)把各次發(fā)送的數(shù)據(jù)重新組合,可能合并也可能拆分,但發(fā)送次序是不變的。

3、在各種情況下都要根據(jù)send的返回值來確定發(fā)送了多少數(shù)據(jù),沒有發(fā)送完就再接著發(fā)。

4、socket是Windows提供網(wǎng)絡(luò)編程接口,TCP/IP是網(wǎng)絡(luò)傳輸協(xié)議,使用socket是可以使用多種協(xié)議,其中包括TCP/IP。


這個(gè)問題第5個(gè)回答:

發(fā)送的過程是:發(fā)送到緩沖區(qū)和從緩沖區(qū)發(fā)送到網(wǎng)絡(luò)上

WSAEWOULDBLOCK和粘包都是出現(xiàn)在發(fā)送到緩沖區(qū)這個(gè)過程的


Socket編程 (異步通訊,解決Tcp粘包)

前面提到,TCP會(huì)出現(xiàn)粘包問題,下面將以實(shí)例演示解決方案:

問題一般會(huì)出現(xiàn)的情況如下,假設(shè)我們連續(xù)發(fā)送兩條兩天記錄("我是liger_zql"):

模擬發(fā)送示例:

?

#region?測(cè)試消息發(fā)送,并匹配協(xié)議

?TcpClient client =new?TcpClient();

?client.AsynConnect();

?Console.WriteLine("下面將連續(xù)發(fā)送2條測(cè)試消息...");

?Console.ReadKey();

?MessageProtocol msgPro;

??for?(int?i =?0; i<2; i++)

? {

? ???msgPro =newMessageProtocol("我是liger_zql");

???? Console.WriteLine("第{0}條:{1}", i +1,msgPro.MessageInfo.Content);

? ???client.AsynSend(msgPro);

? }

??#endregion

?

接收端接受兩條信息會(huì)出現(xiàn)如下三種情況:

1.(1)我是liger_zql(2)我是liger_zql

2.(1)我是liger_zql我是(2)liger_zql

3.(1)我是liger_zql我是liger_zql

通過以上三種情況,顯然2、3都不是我們想要的結(jié)果。那么如何處理這中情況呢?

?

解決方案:通過自定義協(xié)議...

我們可以以將信息以xml的格式發(fā)送出去,列入<protocol>content</protocol>通過正則匹配信息是否完整,如果不完整,我們可以先將本次接受信息緩存接受下一次信息,再次匹配得到相應(yīng)的結(jié)果。

(1)將信息對(duì)象轉(zhuǎn)換成一定格式的xml字符串:

(2)對(duì)接收的信息通過正則進(jìn)行匹配處理:

(3)將該定義的協(xié)議換換成信息對(duì)象,通過對(duì)象獲取自己想要的信息。

結(jié)果:

最后運(yùn)行結(jié)果如下

?

附上源碼:SocketProQuests.zip

詳細(xì)可參考:http://www.cnblogs.com/zengqinglei/archive/2013/05/14/3078842.html

?

?

附:

關(guān)于Socket/TCP粘包、多包和少包, 斷包:http://tsing01.blog.163.com/blog/static/2059572832012716103711125/

?

關(guān)于Tcp封包粘包問題:http://www.cnblogs.com/jiangtong/archive/2012/03/22/2411985.html

TCP通訊處理粘包詳解:http://www.cnblogs.com/smark/p/3284756.html

?

總結(jié)

以上是生活随笔為你收集整理的TCP粘包问题分析和解决(全)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。