粘包現(xiàn)象展示
一上來(lái)就枯燥的文字,難免容易犯困,所以我先演示一下粘包現(xiàn)象:
我們的預(yù)期是,服務(wù)端打印出hello,I am Toranto這兩個(gè)字符串,兩個(gè)字符串經(jīng)編碼之后的字節(jié)總長(zhǎng)度為17個(gè)字節(jié),但是我們看結(jié)果:
ps:b''是表示字節(jié)的意思,和我們之前用的f''(format)格式化用法是一樣的,單、雙、三引號(hào)一樣。可以了解一下:
好了回歸粘包現(xiàn)象,我們看到上圖,我們采用兩次接收數(shù)據(jù)的方式,兩次都接收20個(gè)字節(jié),但是發(fā)送端發(fā)送的兩次總字節(jié)只有17個(gè)字節(jié)長(zhǎng)度,所以,tcp基于流式的協(xié)議,會(huì)先發(fā)送第一段字節(jié)到一個(gè)緩沖區(qū),如果時(shí)間間隔很短,則tcp會(huì)等第二段字也發(fā)送到這個(gè)緩沖區(qū)再統(tǒng)一發(fā)送到接收端這邊,兩次內(nèi)容的粘合沒(méi)有達(dá)到我們預(yù)期的效果,這就是粘包現(xiàn)象。
粘包
粘包是一種現(xiàn)象,而且只有tcp會(huì)出現(xiàn)粘包現(xiàn)象,udp不會(huì),這是基于tcp的
流式傳輸
導(dǎo)致的,tcp是傳輸數(shù)據(jù)流。
tcp會(huì)將數(shù)據(jù)量較小,且發(fā)送時(shí)間間隔較短的數(shù)據(jù)一起打包發(fā)送,那么這里所講的時(shí)間較短是相比網(wǎng)絡(luò)延遲來(lái)說(shuō)的。比如我們兩次發(fā)送間隔為0.00001s,那么網(wǎng)絡(luò)延遲為0.001s,這個(gè)時(shí)候兩次的數(shù)據(jù)就會(huì)打包發(fā)送,這是一種優(yōu)化機(jī)制,但也就是這個(gè)優(yōu)化機(jī)制導(dǎo)致粘包現(xiàn)象。
首先我們需要了解一下socket收發(fā)數(shù)據(jù)的原理:
發(fā)送端可以是1K1K地發(fā)送數(shù)據(jù),而接收端的應(yīng)用程序可以兩K兩K地提走數(shù)據(jù),當(dāng)然也有可能一次提走3K或6K數(shù)據(jù),或者一次只提走幾個(gè)字節(jié)的數(shù)據(jù),也就是說(shuō),應(yīng)用程序所看到的數(shù)據(jù)是一個(gè)整體,或說(shuō)是一個(gè)流(stream),一條消息有多少字節(jié)對(duì)應(yīng)用程序是不可見的,因此TCP協(xié)議是面向流的協(xié)議,這也是容易出現(xiàn)粘包問(wèn)題的原因。
而UDP是面向消息的協(xié)議,每個(gè)UDP段都是一條消息,應(yīng)用程序必須以消息為單位提取數(shù)據(jù),不能一次提取任意字節(jié)的數(shù)據(jù),這一點(diǎn)和TCP是很不同的。怎樣定義消息呢?可以認(rèn)為對(duì)方一次性write/send的數(shù)據(jù)為一個(gè)消息,需要明白的是當(dāng)對(duì)方send一條信息的時(shí)候,無(wú)論底層怎樣分段分片,TCP協(xié)議層會(huì)把構(gòu)成整條消息的數(shù)據(jù)段排序完成后才呈現(xiàn)在內(nèi)核緩沖區(qū)。
例如基于tcp的套接字客戶端往服務(wù)端上傳文件,發(fā)送時(shí)文件內(nèi)容是按照一段一段的字節(jié)流發(fā)送的,在接收方看來(lái),根本不知道該文件的字節(jié)流從何處開始,在何處結(jié)束。
所謂粘包問(wèn)題主要還是因?yàn)榻邮辗讲恢老⒅g的界限,不知道一次性提取多少字節(jié)的數(shù)據(jù)所造成的。
此外,發(fā)送方引起的粘包是由TCP協(xié)議本身造成的,TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一個(gè)TCP段。若連續(xù)幾次需要send的數(shù)據(jù)都很少,通常TCP會(huì)根據(jù)優(yōu)化算法把這些數(shù)據(jù)合成一個(gè)TCP段后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)。
粘包解決
很遺憾,socket并沒(méi)有給我們提供內(nèi)置解決方法。
那我們需要如何解決?
問(wèn)題的根源在于,接收端不知道發(fā)送端將要發(fā)送的字節(jié)流的長(zhǎng)度。
我們可以基于此點(diǎn)想解決辦法,如何讓接收端知道發(fā)送端將要發(fā)送的字節(jié)流長(zhǎng)度(提前獲知)?
我們可以在發(fā)送端寫一個(gè)提前告知的代碼,并且在接收端循環(huán)判定,是否達(dá)到預(yù)定字節(jié)流長(zhǎng)度,達(dá)到了再一并打印出來(lái):
發(fā)送端和接收端(關(guān)鍵)要結(jié)合起來(lái)看:
接下來(lái)我把源碼放上來(lái),我加了一些判斷條件:
演示一下結(jié)果:
審核編輯:劉清
-
編解碼
+關(guān)注
關(guān)注
1文章
140瀏覽量
19662 -
字符串
+關(guān)注
關(guān)注
1文章
585瀏覽量
20604 -
網(wǎng)絡(luò)編程
+關(guān)注
關(guān)注
0文章
72瀏覽量
10104 -
TCP協(xié)議
+關(guān)注
關(guān)注
1文章
101瀏覽量
12124
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
FPGA高速收發(fā)器的GTX發(fā)送端解析
![FPGA高速收發(fā)器的GTX<b class='flag-5'>發(fā)送</b><b class='flag-5'>端</b>解析](https://file.elecfans.com/web1/M00/C8/51/pIYBAF9t9tuAeSiQAAEDcwsl9uM381.jpg)
ZYNQ進(jìn)階:PL端UART 發(fā)送設(shè)計(jì)案例
![ZYNQ進(jìn)階:PL<b class='flag-5'>端</b>UART <b class='flag-5'>發(fā)送</b>設(shè)計(jì)案例](https://file.elecfans.com/web1/M00/C7/DE/o4YBAF9t_NyAc2kFAAC11QhUL20611.png)
HDMI信號(hào)的接收端和發(fā)送端與PD的DFP和UFP有對(duì)應(yīng)關(guān)系嗎?
TCP通信的接收發(fā)送字符串的這個(gè)原理是什么啊?謝謝!
can總線發(fā)送大于八個(gè)字節(jié)數(shù)據(jù)代碼怎實(shí)現(xiàn)
LWIP UDP如何發(fā)送長(zhǎng)度超過(guò)1500字節(jié)的數(shù)據(jù)?
如何解決lwip的netconn客戶端發(fā)送問(wèn)題
CC2500不能接收到發(fā)送端數(shù)據(jù)
如何在發(fā)送端發(fā)送逗號(hào)?
如何讓兩片89C51實(shí)現(xiàn)串行通信,發(fā)送端將0~f循環(huán)發(fā)送到接收端并在接收端顯示?
在消息列隊(duì)中初始化時(shí)怎么知道發(fā)送消息的長(zhǎng)度呢
載波發(fā)送端與接收端接口電路
![載波<b class='flag-5'>發(fā)送</b><b class='flag-5'>端</b>與<b class='flag-5'>接收</b>端接口電路](https://file1.elecfans.com//web2/M00/A4/6A/wKgZomUMNBuABVzsAABfeAxmq1w607.jpg)
評(píng)論