Golang實現簡單tcp服務器04 -- 服務器的粘包處理

服務器的粘包處理

什麼是粘包php

一個完成的消息可能會被TCP拆分紅多個包進行發送,也有可能把多個小的包封裝成一個大的數據包發送,這個就是TCP的拆包和封包問題git

TCP粘包和拆包產生的緣由服務器

  1. 應用程序寫入數據的字節大小大於套接字發送緩衝區的大小併發

  2. 進行MSS大小的TCP分段。MSS是最大報文段長度的縮寫。MSS是TCP報文段中的數據字段的最大長度。數據字段加上TCP首部纔等於整個的TCP報文段。因此MSS並非TCP報文段的最大長度,而是:MSS=TCP報文段長度-TCP首部長度tcp

  3. 以太網的payload大於MTU進行IP分片。MTU指:一種通訊協議的某一層上面所能經過的最大數據包大小。若是IP層有一個數據包要傳,並且數據的長度比鏈路層的MTU大,那麼IP層就會進行分片,把數據包分紅託乾片,讓每一片都不超過MTU。注意,IP分片能夠發生在原始發送端主機上,也能夠發生在中間路由器上。大數據

TCP粘包和拆包的解決策略.net

  1. 消息定長。例如100字節。
  2. 在包尾部增長回車或者空格符等特殊字符進行分割,典型的如FTP協議
  3. 將消息分爲消息頭和消息尾。
  4. 其它複雜的協議,如RTMP協議等。

參考(http://blog.csdn.net/initphp/article/details/41948919)code

咱們的處理方式server

解決粘包問題有多種多樣的方式, 咱們這裏的作法是:blog

  • 發送方在每次發送消息時將消息長度寫入一個int32做爲包頭一併發送出去, 咱們稱之爲Encode
  • 接受方則先讀取一個int32的長度的消息長度信息, 再根據長度讀取相應長的byte數據, 稱之爲Decode

在實驗環境中的主文件夾內, 新建一個名爲codec的文件夾在其之下新建一個文件codec.go, 將咱們的Encode和Decode方法寫入其中, 這裏給出Encode與Decode相應的代碼:

codec.go

package codec

import (
	"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
}

這裏就不帖服務器與客戶端的調用代碼了, 同窗們本身動手試試~

相關源碼: https://git.oschina.net/victoriest/go-simple-tcp-server.git

相關文章
相關標籤/搜索