這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
伺服器的粘包處理
什麼是粘包
一個完成的訊息可能會被TCP拆分成多個包進行發送,也有可能把多個小的包封裝成一個大的資料包發送,這個就是TCP的拆包和封包問題
TCP粘包和拆包產生的原因
應用程式寫入資料的位元組大小大於通訊端發送緩衝區的大小
進行MSS大小的TCP分段。MSS是最大報文段長度的縮寫。MSS是TCP報文段中的資料欄位的最大長度。資料欄位加上TCP首部才等於整個的TCP報文段。所以MSS並不是TCP報文段的最大長度,而是:MSS=TCP報文段長度-TCP首部長度
乙太網路的payload大於MTU進行IP分區。MTU指:一種通訊協定的某一層上面所能通過的最大資料包大小。如果IP層有一個資料包要傳,而且資料的長度比鏈路層的MTU大,那麼IP層就會進行分區,把資料包分成托乾片,讓每一片都不超過MTU。注意,IP分區可以發生在原始發送端主機上,也可以發生在中間路由器上。
TCP粘包和拆包的解決方案策略
- 訊息定長。例如100位元組。
- 在包尾部增加斷行符號或者空格符等特殊字元進行分割,典型的如FTP協議
- 將訊息分為訊息頭和訊息尾。
- 其它複雜的協議,如RTMP協議等。
參考(http://blog.csdn.net/initphp/article/details/41948919)
我們的處理方式
解決粘包問題有多種多樣的方式, 我們這裡的做法是:
- 發送方在每次發送訊息時將訊息長度寫入一個int32作為包頭一併發送出去, 我們稱之為Encode
- 接受方則先讀取一個int32的長度的訊息長度資訊, 再根據長度讀取相應長的byte資料, 稱之為Decode
在實驗環境中的主資料夾內, 建立一個名為codec的檔案夾在其之下建立一個檔案codec.go, 將我們的Encode和Decode方法寫入其中, 這裡給出Encode與Decode相應的代碼:
codec.go
package codecimport ( "bufio" "bytes" "encoding/binary")func Encode(message string) ([]byte, error) { // 讀取訊息的長度 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 } // 寫入訊息實體 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) { // 讀取訊息的長度 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 } // 讀取訊息真正的內容 pack := make([]byte, int(4+length)) _, err = reader.Read(pack) if err != nil { return "", err } return string(pack[4:]), nil}
這裡就不帖伺服器與用戶端的調用代碼了, 同學們自己動手試試~