粘包和分包是利用Socket在TCP協(xié)議下內(nèi)部的優(yōu)化機(jī)制。
為蒙陰等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及蒙陰網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站制作、成都網(wǎng)站制作、蒙陰網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
1、什么是粘包
只有TCP有粘包現(xiàn)象,UDP永遠(yuǎn)不會(huì)粘包,為何,且聽我娓娓道來。發(fā)送數(shù)據(jù)時(shí)間間隔很短,數(shù)據(jù)了很小,也就是發(fā)送數(shù)據(jù)比較頻繁,會(huì)合到一起,產(chǎn)生粘包;
2、什么是分包
當(dāng)我們發(fā)送的數(shù)據(jù)量很大的時(shí)候,可能是幾千字節(jié),TCP就會(huì)自動(dòng)分開發(fā)送,其實(shí)說通俗點(diǎn),就是你去拿快遞,一看20個(gè),一次拿不完,分幾次拿!
3、總結(jié)
指發(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)行分包處理。
在前面的測(cè)試程序中,是沒有粘包問題的,這時(shí)候你可能有疑惑,我為啥數(shù)據(jù)會(huì)發(fā)送的特別快,我們以游戲服務(wù)器舉例,比如游戲有聯(lián)機(jī)對(duì)戰(zhàn)功能,這時(shí)候肯定是需要同步位置信息的,這個(gè)頻率是很快的,大約每秒就要40~80次,這個(gè)時(shí)候就會(huì)出現(xiàn)粘包問題。
其實(shí)很簡(jiǎn)單只要簡(jiǎn)單修改一下客戶端即可。
1、程序測(cè)試 — 粘包問題
客戶端:
服務(wù)器查看調(diào)試信息:
服務(wù)器已啟動(dòng)......
有一個(gè)客戶端進(jìn)行連接成功......
從客戶端接收到的數(shù)據(jù):0
從客戶端接收到的數(shù)據(jù):123
從客戶端接收到的數(shù)據(jù):4567
從客戶端接收到的數(shù)據(jù):8910
從客戶端接收到的數(shù)據(jù):1112131415
從客戶端接收到的數(shù)據(jù):161718
從客戶端接收到的數(shù)據(jù):192021222324
從客戶端接收到的數(shù)據(jù):25262728
從客戶端接收到的數(shù)據(jù):2930313233
從客戶端接收到的數(shù)據(jù):34353637
從客戶端接收到的數(shù)據(jù):38394041
從客戶端接收到的數(shù)據(jù):42434445
從客戶端接收到的數(shù)據(jù):46474849
從客戶端接收到的數(shù)據(jù):50515253
從客戶端接收到的數(shù)據(jù):5455565758
從客戶端接收到的數(shù)據(jù):59606162636465666768
從客戶端接收到的數(shù)據(jù):6970717273
從客戶端接收到的數(shù)據(jù):74757677
從客戶端接收到的數(shù)據(jù):78798081
從客戶端接收到的數(shù)據(jù):82838485
從客戶端接收到的數(shù)據(jù):86878889
從客戶端接收到的數(shù)據(jù):90919293
從客戶端接收到的數(shù)據(jù):9495969798
從客戶端接收到的數(shù)據(jù):99
(很明顯數(shù)據(jù)沒有發(fā)送100次)
1、程序測(cè)試 — 粘包問題
客戶端:
服務(wù)器查看調(diào)試信息:
服務(wù)器已啟動(dòng)......
有一個(gè)客戶端進(jìn)行連接成功......
從客戶端接收到的數(shù)據(jù):
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000撒大聲地所多所多所多所多所多所多所多所多所多所000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000撒大聲地所多所多所多所多所多所多所多所多所多所多所多所多所多所多撒大聲地所 多所多所多所多所多所多所多所多所多所多所多所多所多所多撒大聲地所多所多所多所多所多所多所多所多所多所多所多所多所多所多000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000----------------------------------------------------------------撒大聲地所多所多所多所多所多所多所多所多所?
從客戶端接收到的數(shù)據(jù):
77777777777777777777777777777777777777777777777777777777790909090909090909090909090909090909090000000000000000000000000099
(可以看出服務(wù)器是分兩次接收的,但其實(shí)只要static byte[] dataBuffer = new byte[1024];給的空間足夠大,分包問題就可解決)
其實(shí)也很好解決,我們?cè)诎l(fā)送數(shù)據(jù)的時(shí)候事先存儲(chǔ)數(shù)據(jù)的長(zhǎng)度,不過用來存儲(chǔ)數(shù)據(jù)長(zhǎng)度的內(nèi)存大小需要指定好,否則就沒法判斷了。
假設(shè)我們現(xiàn)在的數(shù)據(jù)出現(xiàn)了粘包,如下圖所示:
這里只是演示一下,如果只有連續(xù)發(fā)送4次數(shù)據(jù),一般是不會(huì)出現(xiàn)粘包的,看上圖橙色部分表示我們用一個(gè)int32類型儲(chǔ)存數(shù)據(jù)的長(zhǎng)度,藍(lán)色部分為我們實(shí)際要發(fā)送的數(shù)據(jù),現(xiàn)在發(fā)生了粘包,也就是這四條數(shù)據(jù)合在一起發(fā)送給了服務(wù)器,
此時(shí)這條數(shù)據(jù)的總大小為 4字節(jié) * 4 + 5 + 7 + 10 + 4 = 42字節(jié)
我們通過讀取4字節(jié)數(shù)據(jù)可以知道數(shù)據(jù)的實(shí)際長(zhǎng)度,以第一個(gè)數(shù)據(jù)為例,我們讀取4字節(jié)數(shù)據(jù),知道了這個(gè)數(shù)據(jù)有5個(gè)字節(jié),程序如下:
int data_length = BitConverter.ToInt32(_data, 0);
此時(shí)的data_length = 5;此時(shí)我們就讀取這5個(gè)字節(jié)的數(shù)據(jù)即可!
string s = Encoding.UTF8.GetString(_data, 4, 5);
然后我們截取數(shù)據(jù),從源數(shù)據(jù)的第4 + 5的位置開始截取到一個(gè)新數(shù)組,新字節(jié)數(shù)組索引從零開始,此時(shí)新字節(jié)數(shù)據(jù)的長(zhǎng)度為42 - (5 + 4);(下圖為新字節(jié)數(shù)組)
Array.Copy(_data, 5 + 4, _data, 0, 42 - (5 + 4));
依次循環(huán)下去,粘包就被成功的分包了。當(dāng)然這個(gè)不要忘記每次更新一下當(dāng)前數(shù)據(jù)長(zhǎng)度。
_curLength = _curLength - (data_length + 4); // _curLength = 42 - (5 + 4)
1、客戶端
創(chuàng)建Message類,用于發(fā)送數(shù)據(jù)前做處理,使得首4字節(jié)儲(chǔ)存數(shù)據(jù)長(zhǎng)度。
Message:
Main:
2、服務(wù)端
創(chuàng)建Message類,解決粘包問題!
Main:
服務(wù)器查看調(diào)試信息:
服務(wù)器已啟動(dòng)......
有一個(gè)客戶端進(jìn)行連接成功......
解析到一條數(shù)據(jù):0米
4
8
解析到一條數(shù)據(jù):1米
4
630
解析到一條數(shù)據(jù):2米
4
622
解析到一條數(shù)據(jù):3米
4
614
解析到一條數(shù)據(jù):4米
4
606
解析到一條數(shù)據(jù):5米
4
598
解析到一條數(shù)據(jù):6米
4
590
解析到一條數(shù)據(jù):7米
4
582
解析到一條數(shù)據(jù):8米
4
574
解析到一條數(shù)據(jù):9米
4
566
解析到一條數(shù)據(jù):10米
5
558
..............................................................................
解析到一條數(shù)據(jù):98米
5
18
解析到一條數(shù)據(jù):99米
5
9
有客戶端退出.....
你解決個(gè)屁,異步接收的情況下,把_data數(shù)組調(diào)大點(diǎn)就完了,傻逼,咱們是做游戲!一般不會(huì)有分包問題??!
Java更好,因?yàn)镴ava就業(yè)更加廣泛,雖說學(xué)習(xí)web前端不需要學(xué)java,但是會(huì)java肯定更好,這樣你才能慢慢地成為一個(gè)全棧工程師 。千鋒教育就有線上免費(fèi)Java線上公開課 你要是想在網(wǎng)頁上實(shí)現(xiàn)交互效果,比如輪播圖、點(diǎn)擊按鈕后播放動(dòng)畫等等,那么就必須學(xué)會(huì)Java。如果你還處于迷茫階段或者是一個(gè)小白,那你不要著急著手開始學(xué)習(xí),沒有目的和系統(tǒng)的學(xué)習(xí)方法,只會(huì)走彎路。建議你先深入了解有關(guān)前端方面的所有資訊,包括前景、就業(yè)、行業(yè)發(fā)展、技術(shù)囊括、職業(yè)等等,這樣才能有目的和方向的學(xué)習(xí)。如果你想好要學(xué)習(xí)web前端和java,推薦看看線上免費(fèi)視頻,多在網(wǎng)上查詢相關(guān)視頻和機(jī)構(gòu),選擇最適合自己的學(xué)習(xí)方法,要是不想線上學(xué)習(xí)可以向本地區(qū)的千鋒教育機(jī)構(gòu)進(jìn)行線下咨詢。千鋒教育總部位于北京,在18個(gè)城市擁有22個(gè)校區(qū),講師均來自一線大廠兼具項(xiàng)目實(shí)戰(zhàn)與教學(xué)經(jīng)驗(yàn),年培養(yǎng)優(yōu)質(zhì)人才20000余人,與國內(nèi)20000余家企業(yè)建立人才輸送合作關(guān)系,院校合作超600所。
一、socket 通信粘包的處理方法:
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)字段,人為控制分多次接收,然后合并,通過這種手段來避免粘包。
二、實(shí)現(xiàn)代碼:
三、方法注意事項(xiàng):
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)合不適合。
四、實(shí)驗(yàn)環(huán)境
1、硬件環(huán)境:服務(wù)器:pentium 350 微機(jī) 、客戶機(jī):pentium 166微機(jī)、網(wǎng)絡(luò)平臺(tái):由10兆共享式hub連接而成的局域網(wǎng);
2、軟件環(huán)境:操作系統(tǒng):windows 98 、編程語言:visual c++ 5.0
tcp是流形式的,所以無法知道每個(gè)數(shù)據(jù)包的分界,特別是在大量小數(shù)據(jù)包的時(shí)候,容易一次接收N個(gè)數(shù)據(jù)包。在數(shù)據(jù)包大的時(shí)候容易一次沒接收完數(shù)據(jù)包。解決辦法一般都是使用包頭+數(shù)據(jù)的形式,包頭包含數(shù)據(jù)的長(zhǎng)度信息,一般可以用個(gè)int,固定長(zhǎng)度為4包頭=數(shù)據(jù)長(zhǎng)度首先把包頭發(fā)送過去,在發(fā)送數(shù)據(jù)。由于包頭的長(zhǎng)度是固定的(sizeof(int)),所以接收端,只要先接收4個(gè)字節(jié)的包頭,再接收包頭里長(zhǎng)度的數(shù)據(jù)包!重復(fù)這樣的操作,可以解決分包和粘包的問題