什么是粘包
一個(gè)完成的消息可能會(huì)被TCP拆分成多個(gè)包進(jìn)行發(fā)送,也有可能把多個(gè)小的包封裝成一個(gè)大的數(shù)據(jù)包發(fā)送,這個(gè)就是TCP的拆包和封包問(wèn)題
TCP粘包和拆包產(chǎn)生的原因
應(yīng)用程序?qū)懭霐?shù)據(jù)的字節(jié)大小大于套接字發(fā)送緩沖區(qū)的大小
進(jìn)行MSS大小的TCP分段。MSS是最大報(bào)文段長(zhǎng)度的縮寫。MSS是TCP報(bào)文段中的數(shù)據(jù)字段的最大長(zhǎng)度。數(shù)據(jù)字段加上TCP首部才等于整個(gè)的TCP報(bào)文段。所以MSS并不是TCP報(bào)文段的最大長(zhǎng)度,而是:MSS=TCP報(bào)文段長(zhǎng)度-TCP首部長(zhǎng)度
以太網(wǎng)的payload大于MTU進(jìn)行IP分片。MTU指:一種通信協(xié)議的某一層上面所能通過(guò)的最大數(shù)據(jù)包大小。如果IP層有一個(gè)數(shù)據(jù)包要傳,而且數(shù)據(jù)的長(zhǎng)度比鏈路層的MTU大,那么IP層就會(huì)進(jìn)行分片,把數(shù)據(jù)包分成托干片,讓每一片都不超過(guò)MTU。注意,IP分片可以發(fā)生在原始發(fā)送端主機(jī)上,也可以發(fā)生在中間路由器上。
TCP粘包和拆包的解決策略
參考文章 (http://blog.csdn.net/initphp/article/details/41948919)
我們的處理方式
解決粘包問(wèn)題有多種多樣的方式, 我們這里的做法是:
在實(shí)驗(yàn)環(huán)境中的主文件夾內(nèi), 新建一個(gè)名為codec的文件夾在其之下新建一個(gè)文件codec.go, 將我們的Encode和Decode方法寫入其中, 這里給出Encode與Decode相應(yīng)的代碼:
codec.go
package codecimport ( "bufio" "bytes" "encoding/binary")func Encode(message string) ([]byte, error) { // 讀取消息的長(zhǎng)度 var length int32 = int32(len(message)) var pkg *bytes.Buffer = new(bytes.Buffer) // 寫入消息頭 err := binary.Write(pkg, binary.LittleEndian, length) if err != nil { return nil, err } // 寫入消息實(shí)體 err = binary.Write(pkg, binary.LittleEndian, []byte(message)) if err != nil { return nil, err } return pkg.Bytes(), nil}func Decode(reader *bufio.Reader) (string, error) { // 讀取消息的長(zhǎng)度 lengthByte, _ := reader.Peek(4) lengthBuff := bytes.NewBuffer(lengthByte) var length int32 err := binary.Read(lengthBuff, binary.LittleEndian, &length) if err != nil { return "", err } if int32(reader.Buffered()) < length+4 { return "", err } // 讀取消息真正的內(nèi)容 pack := make([]byte, int(4+length)) _, err = reader.Read(pack) if err != nil { return "", err } return string(pack[4:]), nil}
這里就不帖服務(wù)器與客戶端的調(diào)用代碼了, 同學(xué)們自己動(dòng)手試試~
聯(lián)系客服