1. 程式人生 > >處理粘包和半包有關問題的socket分包Java實現

處理粘包和半包有關問題的socket分包Java實現

- 處理粘包和半包問題的socket分包Java實現
只知道原理,程式碼實現還不知道怎麼實現?請高手指點,謝謝!高分饋贈!

------解決方案--------------------
一般在socket處理大資料量傳輸的時候會產生粘包和半包問題,有的時候tcp為了提高效率會緩衝N個包後再一起發出去,這個與快取和網路有關係

在java中對於這樣的優化,lz可以用非阻塞的流操作,非阻塞socket和流操作都是nio包中

個人建議,呵呵,沒有去認真的考慮過這個問題


------解決方案--------------------
可能解決 粘包的問題。

Socket socket = new Socket();
socket.setTcpNoDelay(true);

------解決方案--------------------
譬如 粘包 為x.5個包 半包 為0.5個包 由於網路原因 一次可能會來 0.5/1 /2/ 2.5/ 。。。。個包 當接收到時 要先看看那這個包中有多少個完整的包。把完整的包都處理了 也就是說把x都處理了。剩下的0.5留在接收區中,等待下次接收。 這回接收到的就是0.5+1.5/0.5+1.3/0.5+0.5..... 把完整的包都處理了,有殘缺的扔掉 0.8的。 一般情況 接收到正確的後都要給傳送端一個應答。不給應答的算超時,傳送端將重發。 有頭沒尾的不能扔 沒頭有尾的可以扔 有頭有尾但缺東西可以扔 有頭有尾不缺東西不能扔 ------解決方案-------------------- 之所以出現粘包和半包現象,是因為TCP當中,只有流的概念,沒有包的概念. 樓主可以使用UDP協議.這樣可以就可以區分每個包了.但是要確保包的丟失處理.為了提到效率,可以考慮寫一個滑動視窗進行收發包. 若採用TCP協議進行傳輸,就要將每個包區分開來.可以有三種方式.因為TCP是面向流的.流只有開啟和關閉,你要用一個流傳輸多個包,那就要向辦法區分出每個包. 一:: 可以每次傳送同樣大小的包,過大的包不予傳送,過小的包,後面部分用固定的字元'\0'進行填充. 二:: 將流按字元處理,抽出一個字元做轉義字元(通常Java用'\'來做轉義字元,比如"\n"表示換行).假如就設'\'為轉義字元,傳送方如果流當中出現'\',就在後面在追加一個'\',如果包結束,則用'\'做包的結束符.這樣,在接收方,若讀取一個單獨的'\'或者流結束,就標示前面的內容構成一個包,如果連續讀取兩個'\',就將兩個'\'用一個'\'進行替換.這樣,就可以保證原來包中的資訊不變,同時也能區分出每個包了. 三:: 在傳送方傳送一個包的時候,先將這個包的長度傳送給對方(一般是4個位元組表示包長),然後再將包的內容傳送過去.接收方先接收4個位元組,看看包的長度,然後按照長度來接收包,這樣就不會出錯了. 以上三種方法,是網路傳輸中經常用到的方法.後兩種很常見.最後一種,在TCP長連線傳輸中應用最多. 綜合以上的說法,就是要在TCP協議以上再封裝一層協議,用來做分包的資訊交換. ------解決方案--------------------
當然,如果TCP不是非要長連線,或者,資訊包不是批量傳輸的情況下.可以一次TCP連線只傳輸一個包.這種情況下一般,一次TCP完成一次互動,即傳送方傳送資訊包,接收方接收資訊包同時傳送一個接收方的響應包給傳送方,表明接收方收到資訊包,還是收到了錯誤包,或者接收方系統異常沒有處理這個包之類的資訊. 其實HTTP的互動過程,就是非常類似這樣的. 當然這種情況,必須要求傳送方和接收方互為伺服器客戶端,否則資訊就是單向的了. ------解決方案-------------------- 我一般處理是: 一個BUFFER,用於儲存當前連線的讀快取 有資料時,Buffer = Buffer + DataIn,不停的接收 收完成後,開始解析Buffer, 根據包的協議,不停的解析Buffer,並形成一個個包進行處理,處理後,Buffer = Buffer - Data,並繼續解包 完成。 ------解決方案--------------------
喜歡使用Buffer,把資料先壓入,而後按照協議去解析,這樣就繞開了半包等問題 ------解決方案-------------------- 一般協議設計,每個包不是以特定標誌為結尾,就是把長度宣告在開頭。 ------解決方案-------------------- 尊敬樓主!能將自己的想法說的如此清晰詳細已備後來者學習,值得尊敬!學習中... ------解決方案-------------------- JDK裡面.BufferedReader是用來處理字元流的.在網路通訊當中,一般不用這個類. 而用這個類來處理的.一般是我講到的第二種處理方式.只不過,是用換行符作為包的分隔符(我講的第二方式比較通用,特例的情況下如果傳輸的是ACSII字元流,可以指定換行符為包的分隔符).接收端使用BufferedReader的readLine()方法.傳送端在傳送字串之後,再追發一個換行符'\n',用於進行包的分隔. 樓主可以試一下,這個效率會很高,而且,不會有粘包,半包的現象. 由於網路傳輸資料的不確定性(也就是說有可能傳輸的是影象,檔案什麼的),所以,一般直接使用InputStream這個類來操作.它的read方法都是阻塞讀的.引數為byte陣列的情況下,如果流沒有結束,該方法直到byte陣列填滿才會返回.樓主可以試一下,直接用InputStream這個類來處理.就不會出現使用BufferedReader讀出半包的情況了.當然也不用再去遞迴補讀了. 直接使用InputStream一般效率還是很高的,如果還要提高效率,那就要自己編寫一個緩衝區了.使用多執行緒(執行緒數量3個就可以)併發處理.效能會顯著提高的. ------解決方案-------------------- socket在通訊處理中資料包一分為二或二合為一的狀況很多,而且不好模擬測試. 處理方法一般有二: 1.定長法. 每次傳送資料都定長. 多餘的資料作為下一個包的起始部分保留. 收不夠長度就儲存起來, 知道夠資料為止 2.起始符法. 每個資料都有特定的起始符號內的特定資料, 如果起始符範圍外有資料,可以確認是程式邏輯有問題或有人搞破壞了. 上面的方法開工前, 都要制定你的應用協議. 原理清楚了, 協議搞完整了, 程式就好寫了. ------解決方案-------------------- 29樓說的 UDP是獨立路由的,這一點,我贊同.  但是,"TCP一旦建立連線後路由路徑就定下來了",這句話,我不贊同.
人理解,TCP在傳輸流的時候,其實是將整個流拆成很多個獨立的IP報文,進行獨立路由傳輸的,只是,到了接收端,接收端會把這些報文按照先後順序重新組裝成TCP的IO流,當然,丟包的話,會有相應的機制來確保傳送端重新發送.
TCP的建立連線階段,本人認為,其實就是傳輸雙達成一個共識,比如雙方的傳送視窗,接收視窗的大小,每個報文的大小,以及超時的時間範圍等等.
因為,實際上,TCP也好,UDP也好,在網路傳輸的過程中都遵循的是IP協議,並且,每個節點的路由也只是遵照IP協議來進行轉發的.
所以,相對來講TCP底層的IP報文,它的路由具有不確定性.
以上是我的愚見... 
------解決方案--------------------
探討 29樓說的 UDP是獨立路由的,這一點,我贊同. 但是,"TCP一旦建立連線後路由路徑就定下來了",這句話,我不贊同. 個人理解,TCP在傳輸流的時候,其實是將整個流拆成很多個獨立的IP報文,進行獨立路由傳輸的,只是,到了接收端,接收端會把這些報文按照先後順序重新組裝成TCP的IO流,當然,丟包的話,會有相應的機制來確保傳送端重新發送. TCP的建立連線階段,本人認為,其實就是傳輸雙達成一個共識,比如雙方的傳送視窗,接收視窗的大小,每個報…
------解決方案-------------------- 1.如果是TCP流傳輸,就定義包文頭與包文體結構,然後通過包文頭中的報文體長度來進行擷取.(報文體為變長,報文頭為定長) 2.如果是UDP,內建支援訊息邊界,不用考慮粘包,只需要考慮可靠性問題. 通常為減少TCP粘包以及提高TCP效率,每個包建議不超過1.3K,以避免MAC層進行分禎 ------解決方案--------------------
探討 引用: 1.你的回覆表示了你連帖子的意思都沒看懂,看看其他人回覆的吧,特別是preferme ,kokobox ,TinyJimmy==回覆的,他們的回覆的內容就是帖子所需要的答案。 2.你這樣的回覆根本沒有任何意義,就好像我們現在問向量機,正交矩陣的問題,然後你跳出來說這都是大學裡學的東西,還好意思來這裡問。… 同意你的看法. 我當時只是一驚,怎麼學校課程裡講的東西都出來了.
------解決方案-------------------- 路過,沒有什麼意見。