首先看兩個概念:
短鏈接:
鏈接->傳輸數據->關閉鏈接
HTTP是無狀態的,瀏覽器和服務器每進行一次HTTP操做,就創建一次鏈接,但任務結束就中斷鏈接。
也能夠這樣說:短鏈接是指SOCKET鏈接後發送後接收完數據後立刻斷開鏈接。
長鏈接:
鏈接->傳輸數據->保持鏈接 -> 傳輸數據-> 。。。 ->關閉鏈接。
長鏈接指創建SOCKET鏈接後不論是否使用都保持鏈接,但安全性較差。
之因此出現粘包和半包現象,是由於TCP當中,只有流的概念,沒有包的概念.
半包
指接受方沒有接受到一個完整的包,只接受了部分,這種狀況主要是因爲TCP爲提升傳輸效率,將一個包分配的足夠大,致使接受方並不能一次接受完。(
在長鏈接和短鏈接中都會出現)。
粘包與分包
指發送方發送的若干包數據到接收方接收時粘成一包,從接收緩衝區看,後一包數據的頭緊接着前一包數據的尾。出現粘包現象的緣由是多方面的,它既可能由發送方形成,也可能由接收方形成。發送方引發的粘包是由TCP協議自己形成的,TCP爲提升傳輸效率,發送方每每要收集到足夠多的數據後才發送一包數據。若連續幾回發送的數據都不多,一般TCP會根據優化算法把這些數據合成一包後一次發送出去,這樣接收方就收到了粘包數據。接收方引發的粘包是因爲接收方用戶進程不及時接收數據,從而致使粘包現象。這是由於接收方先把收到的數據放在系統接收緩衝區,用戶進程從該緩衝區取數據,若下一包數據到達時前一包數據還沒有被用戶進程取走,則下一包數據放到系統接收緩衝區時就接到前一包數據以後,而用戶進程根據預先設定的緩衝區大小從系統接收緩衝區取數據,這樣就一次取到了多包數據。分包是指在出現粘包的時候咱們的接收方要進行分包處理。(在長鏈接中都會出現)
何時須要考慮半包的狀況?
從備註中咱們瞭解到Socket內部默認的收發緩衝區大小大概是8K,可是咱們在實際中每每須要考慮效率問題,從新配置了這個值,來達到系統的最佳狀態。
一個實際中的例子:用mina做爲服務器端,使用的緩存大小爲10k,這裏使用的是短鏈接,全部不用考慮粘包的問題。
問題描述:在併發量比較大的狀況下,就會出現一次接受並不能完整的獲取全部的數據。
處理方式:
1.經過包頭+包長+包體的協議形式,當服務器端獲取到指定的包長時才說明獲取完整。
2.指定包的結束標識,這樣當咱們獲取到指定的標識時,說明包獲取完整。
何時須要考慮粘包的狀況?
1.當時短鏈接的狀況下,不用考慮粘包的狀況
2.若是發送數據無結構,如文件傳輸,這樣發送方只管發送,接收方只管接收存儲就ok,也不用考慮粘包
3.若是雙方創建鏈接,須要在鏈接後一段時間內發送不一樣結構數據
處理方式:
接收方建立一預處理線程,對接收到的數據包進行預處理,將粘連的包分開
注:粘包狀況有兩種,一種是粘在一塊兒的包都是完整的數據包,另外一種狀況是粘在一塊兒的包有不完整的包
備註:
一個包沒有固定長度,以太網限制在46-1500字節,1500就是以太網的MTU,超過這個量,TCP會爲IP數據報設置偏移量進行分片傳輸,如今通常可容許應用層設置8k(NTFS系)的緩衝區,8k的數據由底層分片,而應用看來只是一次發送。windows的緩衝區經驗值是4k,Socket自己分爲兩種,流(TCP)和數據報(UDP),你的問題針對這兩種不一樣使用而結論不同。甚至還和你是用阻塞、仍是非阻塞Socket來編程有關。
一、通訊長度,這個是你本身決定的,沒有系統強迫你要發多大的包,實際應該根據需求和網絡情況來決定。對於TCP,這個長度能夠大點,但要知道,Socket內部默認的收發緩衝區大小大概是8K,你能夠用SetSockOpt來改變。但對於UDP,就不要太大,通常在1024至10K。注意一點,你不管發多大的包,IP層和鏈路層都會把你的包進行分片發送,通常局域網就是1500左右,廣域網就只有幾十字節。分片後的包將通過不一樣的路由到達接收方,對於UDP而言,要是其中一個分片丟失,那麼接收方的IP層將把整個發送包丟棄,這就造成丟包。顯然,要是一個UDP發包佷大,它被分片後,鏈路層丟失分片的概率就佷大,你這個UDP包,就佷容易丟失,可是過小又影響效率。最好能夠配置這個值,以根據不一樣的環境來調整到最佳狀態。
send()函數返回了實際發送的長度,在網絡不斷的狀況下,它毫不會返回(發送失敗的)錯誤,最多就是返回0。對於TCP你能夠字節寫一個循環發送。當send函數返回SOCKET_ERROR時,才標誌着有錯誤。但對於UDP,你不要寫循環發送,不然將給你的接收帶來極大的麻煩。因此UDP須要用SetSockOpt來改變Socket內部Buffer的大小,以能容納你的發包。明確一點,TCP做爲流,發包是不會整包到達的,而是源源不斷的到,那接收方就必須組包。而UDP做爲消息或數據報,它必定是整包到達接收方。 算法
二、關於接收,通常的發包都有包邊界,首要的就是你這個包的長度要讓接收方知道,因而就有個包頭信息,對於TCP,接收方先收這個包頭信息,而後再收包數據。一次收齊整個包也能夠,可要對結果是否收齊進行驗證。這也就完成了組包過程。UDP,那你只能整包接收了。要是你提供的接收Buffer太小,TCP將返回實際接收的長度,餘下的還能夠收,而UDP不一樣的是,餘下的數據被丟棄並返回WSAEMSGSIZE錯誤。注意TCP,要是你提供的Buffer佷大,那麼可能收到的就是多個發包,你必須分離它們,還有就是當Buffer過小,而一次收不完Socket內部的數據,那麼Socket接收事件(OnReceive),可能不會再觸發,使用事件方式進行接收時,密切注意這點。這些特性就是體現了流和數據包的區別。