什么是粘包問題
發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方時(shí)粘成一包,從接受緩沖區(qū)來看就是后一包數(shù)據(jù)的頭緊接著前一包數(shù)據(jù)的尾。
粘包出現(xiàn)的原因
發(fā)送方等待到緩沖區(qū)滿才將包發(fā)送出去
若連續(xù)幾次發(fā)送數(shù)據(jù)都很小,tcp會(huì)根據(jù)優(yōu)化算法把這些數(shù)據(jù)整合成一包后一次性發(fā)送,這樣接收方就收到了粘包數(shù)據(jù)。
接收方不及時(shí)接受緩沖區(qū)的包,造成多個(gè)包接收
接收方會(huì)先把收到的數(shù)據(jù)放在系統(tǒng)接收緩沖區(qū),用戶進(jìn)程從該緩沖區(qū)讀取數(shù)據(jù),若下一包數(shù)據(jù)到達(dá)時(shí)前一包數(shù)據(jù)尚未被用戶進(jìn)程取走,則下一包數(shù)據(jù)進(jìn)入緩沖區(qū)時(shí)就到前一包數(shù)據(jù)之后,而用戶進(jìn)程根據(jù)預(yù)先設(shè)定的緩沖區(qū)大小從系統(tǒng)中讀取數(shù)據(jù),這樣便一次取了很多包。
解決方法--封包和解包
發(fā)送方在發(fā)送數(shù)據(jù)的包前,加入數(shù)據(jù)長(zhǎng)度,將數(shù)據(jù)包的結(jié)構(gòu)變成[dataLen|data]的結(jié)構(gòu)再進(jìn)行發(fā)送。而接收方通過解析這種結(jié)構(gòu),從而將多個(gè)數(shù)據(jù)包聚合形成的粘包分解成一個(gè)個(gè)獨(dú)立的數(shù)據(jù)包。具體核心代碼如下所示:
發(fā)送方封包:
const (
DataLen = 4
)
//封裝數(shù)據(jù)包
func Packet(message []byte) []byte {
return append(IntToBytes(len(message)), message...)
}
接收方解包:
const (
DataLen = 4
)
//解包
func Unpack(buffer []byte, readerChannel chan []byte) []byte {
length := len(buffer)
var i int
for i = 0; i < length; i++ {
if length < i + DataLen {
break
}
//根據(jù)長(zhǎng)度來獲取數(shù)據(jù)
messageLen := BytesToInt(buffer[i:i+DataLen])
if length < i + DataLen + messageLen {
break
}
data := buffer[i+DataLen:i+DataLen+messageLen]
readerChannel <- data
i += DataLen + messageLen - 1
}
if i == length {
return make([]byte, 0)
}
return buffer[i:]
}