- 在一個(gè)項(xiàng)目中,要在TCP連接的客戶端和服務(wù)器之間發(fā)送json形式的數(shù)據(jù)。遇到了一個(gè)問(wèn)題,有時(shí)候數(shù)據(jù)發(fā)送端連續(xù)多次調(diào)用send()函數(shù)發(fā)送多條json字符串時(shí),接收端用recv()函數(shù)接收一條數(shù)據(jù)是一個(gè)組合多條json的大字符串,并不是期望的收到多條分割開(kāi)的json字符串。這就是TCP的粘包問(wèn)題。
- 這就導(dǎo)致在使用cJSON_Parse()函數(shù)對(duì)json字符串進(jìn)行解析時(shí),只能解析出第一條完整的json,而后面的就丟棄了。這種情況對(duì)于每條json是一條命令來(lái)說(shuō),是很糟糕的。
- 其實(shí)這種每條json是一個(gè)命令的業(yè)務(wù)需求,使用UDP來(lái)發(fā)送數(shù)據(jù),就不會(huì)發(fā)生粘包問(wèn)題。UDP是數(shù)據(jù)報(bào)傳輸協(xié)議,顧名思義其就是每次發(fā)送一個(gè)數(shù)據(jù)報(bào),不會(huì)進(jìn)行多條的封裝拼湊再集體發(fā)送。
在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)行封包。這樣,接收端,就難于分辨出來(lái)了,必須自己提供科學(xué)的拆包機(jī)制。
對(duì)于UDP,不會(huì)使用塊的合并優(yōu)化算法,這樣,實(shí)際上目前認(rèn)為,是由于UDP支持的是一對(duì)多的模式,所以接收端的skbuff(套接字緩沖區(qū))采用了鏈?zhǔn)浇Y(jié)構(gòu)來(lái)記錄每一個(gè)到達(dá)的UDP包,在每個(gè)UDP包中就有了消息頭(消息來(lái)源地址,端口等信息),這樣,對(duì)于接收端來(lái)說(shuō),就容易進(jìn)行區(qū)分處理了。所以UDP不會(huì)出現(xiàn)粘包問(wèn)題。
https://www.cnblogs.com/kex1n/p/6502002.html
TCP通信粘包問(wèn)題分析和解決(全)
====================================================================
TCP的Nagle算法和延遲ACK
- Nagle算法的操作過(guò)程請(qǐng)參看Wiki,它減少了大量小包的發(fā)送,實(shí)際上就是基于小包的停-等協(xié)議。在等待已經(jīng)發(fā)出的包被確認(rèn)之前,發(fā)送端利用這段時(shí)間可以積累應(yīng)用下來(lái)的數(shù)據(jù),使其大小趨向于增加。這是避免糊涂窗口綜合癥的一種有效方法,請(qǐng)注意,糊涂窗口指的是接收端的糊涂,而不是發(fā)送端的糊涂,接收端不管三七二十一得通告自己的接收窗口大小,絲毫不管這會(huì)在發(fā)送端產(chǎn)生大量小包。然而發(fā)送端可以不糊涂,你通告你的,我就是不發(fā),你糊涂我不糊涂,你不斷通告很小的數(shù)值,我不予理睬,我有自己的方法,直到收到已經(jīng)發(fā)出包的ACK才會(huì)繼續(xù)發(fā)送,這就是Nagle算法的糊涂抵制方案。
- 治療糊涂窗口綜合癥有兩種方式,一種是“你糊涂我不糊涂”的方式,即上述的Nagle算法的方式,另外一種是“治療接收端的糊涂”的方式,其中一種機(jī)制是延遲ACK(還有其它機(jī)制,比如不發(fā)送小窗口通告等)。可以看出,這兩種方式中都在試圖減少包的發(fā)送量,二者殊途同歸的解決了同一問(wèn)題,對(duì)于發(fā)送方而言,不理會(huì)接收端的小窗口通告等于說(shuō)不馬上發(fā)送小包,小包得以有時(shí)間積累成大包,對(duì)于接收方而言,延遲ACK可以拖延ACK發(fā)送時(shí)間,進(jìn)而延遲窗口通告,在這段時(shí)間內(nèi),接收窗口有機(jī)會(huì)進(jìn)一步(由于應(yīng)用程序處理)放大。單獨(dú)理解這兩種方式都是簡(jiǎn)單的,但是一旦它們混在一起使用,情況就會(huì)非常不幸!因?yàn)?..
- Nagle算法和延遲ACK作用在方向相反的數(shù)據(jù)包和針對(duì)該數(shù)據(jù)包的確認(rèn)包上,因此它們的作用力會(huì)相悖,結(jié)果就是誰(shuí)也不能發(fā)包。就像一根繩子上拴兩只青蛙一樣,被對(duì)方牽制誰(shuí)也跑不了!關(guān)鍵點(diǎn)在于,小包的發(fā)送依賴于ACK,然而延遲ACK阻止了ACK的即時(shí)發(fā)送,形成了僵持狀態(tài)。本來(lái)只是為了減少網(wǎng)絡(luò)上小包的數(shù)量(再次強(qiáng)調(diào)Nagle算法以及延遲ACK的目的,注意,糊涂窗口綜合癥只是網(wǎng)絡(luò)上小包泛濫的原因之一!),卻人為引入了大量的延遲!
- 此處有一個(gè)通用的解釋,Nagle算法的小包發(fā)送依賴于接收端對(duì)小包得快速確認(rèn),因此接收端對(duì)待ACK而言,應(yīng)該朝著延遲ACK相反俄方向用力,即快速ACK;相對(duì)的,如果在接收端啟用了延遲ACK,發(fā)送端就應(yīng)該不斷發(fā)送數(shù)據(jù)包,不管是大包還是小包(不考慮稍帶ACK的影響),因?yàn)榘l(fā)送端已經(jīng)不能指望接收端正常ACK數(shù)據(jù)包了,即發(fā)送端應(yīng)該禁掉Nagle算法。以上解釋背后的思想就是數(shù)據(jù)包和ACK包是相關(guān)的,力應(yīng)該往一個(gè)方向使,一邊拉另一邊就要推,如果兩邊都拉,力就會(huì)抵消掉,陷入僵持。不幸點(diǎn)或者說(shuō)悲哀的地方在于,Nagle算法和延遲ACK機(jī)制都是“拉方案”!劃船的人都知道,劃槳手有兩種座位布局,要么超同一個(gè)方向,要么面朝不同的方向,后者和TCP數(shù)據(jù)包和ACK很類似,要想船往前走,必須朝一個(gè)方向劃,雖然他們面朝相反的方向。
https://blog.csdn.net/dog250/article/details/21303679
再次談?wù)凾CP的Nagle算法與TCP_CORK選項(xiàng)
https://blog.csdn.net/wdscq1234/article/details/52432095
TCP-IP詳解:Nagle算法