寫在這裏一是作一下總結在忘的時候方便查看,二是若是文章有錯誤請各位大佬噴我哈哈,歡迎指正。若是能幫到別人也挺好的。golang
所謂協議就是指定一系列規則,這些規則使想要交流的雙方或多方能夠正常通訊交流。如咱們說的漢語就是協議,若是不按照漢語規則說話,交流的人就聽不懂對方說什麼,漢語和英語就是不一樣的協議,用漢語和只會英語的人交流,人家也聽不懂你說啥。在計算機中,入參出參是協議,最簡單的服務端rest接口是協議,系統實現一層層的網絡協議使計算機能夠互相通訊。網絡
自定義協議就是在現有協議知足不了咱們需求時,在現有協議之上構建的知足咱們本身程序的通訊需求的協議。目前的操做系統,會把咱們應用須要的底層網絡協議實現好,這對咱們來講是透明的,可是在咱們寫rest時,是否是須要定義接口呢,這接口就是咱們自定義的一種協議,經過定義請求參數,響應參數,讓客戶端和咱們寫好的服務端程序進行交互,這就是自定義的協議。socket
在TCP基礎上自定義協議,主要就是通訊雙方規定一個頭,頭後面是傳遞的數據。頭中規定一些參數,好比傳遞的消息前10個字節是頭,頭最前面2個字節是個固定魔數,讓你知道我是根據這個協議來通訊的,若是不支持這個協議的話,就能夠直接關掉這個鏈接了。跟着魔數的4個字節是整個包中數據的長度,這個長度不包括頭,須要長度的緣由是TCP協議面向的是流,會把應用本來一個一個發送的包一股腦的扔給對方,若是不用長度字段,咱們就頗有可能拿到了多個包的數據。最後4個字節是個校驗碼,校驗收到的包的數據是否有錯誤,主要是防止通訊時被篡改。
此時咱們就完成了咱們自定義協議的定義。只須要發送端在將數據寫入TCP前,在數據前面加入這個頭,接收端接收到數據時,按照這個頭的定義拿取數據,就能夠完成雙方的通訊了。函數
自定義了一個Conn結構體,結構體中包含鏈接,元數據,scan是用來從真正的socket中獲取數據,經過socket鏈接new一個scan,而後經過scan的split方法註冊一個分割數據的函數,這裏包裝在了RegisterSplitFunc()函數中,scan會經過咱們註冊函數中的分割方式來分割數據,經過Scan()方法分割一次數據,獲取返回數據使用scan.Bytes()ui
type Conn struct { c net.Conn meta metadata.Metadata scan *bufio.Scanner } func (c *Conn) Read() (b []byte, err error) { if c.c == nil { return nil, errors.New("conn is nil") } if c.scan == nil { //包裝過的函數,作了一個scan.Split(splitFunc)的操做 c.RegisterSplitFunc() } if c.scan.Scan() { b = c.scan.Bytes() } else { c.c.Close() err = errors.New("conn scan error") } return } //write方法中的頭由模數0x0102, 長度len(data)+4組成 //頭後面跟着數據 func (c *Conn) Write(data []byte) (err error) { if len(data) > math.MaxUint16 { return errors.New("data too big") } buf := bytes.Buffer{} binary.Write(&buf, binary.BigEndian, []byte{0x01,0x02}) binary.Write(&buf, binary.BigEndian, uint16(len(data)+4)) binary.Write(&buf, binary.BigEndian, data) _, err = c.c.Write(buf.Bytes()) return }
分割函數操作系統
func MySplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) { //判斷長度,魔數 if len(data) < 4 || data[0] != 0x01 || data[1] != 0x02 { err = errors.New("protocol error") return } var l uint16 //獲取header err = binary.Read(bytes.NewReader(data[2:4]), binary.BigEndian, &l) if err != nil { logs.Error("binary.Read error(%v)", err) return } //經過長度讀取數據,advance爲讀取的長度,包括頭和數據,data是讀取的數據 if int(l) <= len(data) { advance, token, err = int(l), data[:int(l)], nil } if atEOF { err = errors.New("EOF") } return }
以上爲在golang中自定義簡單通訊協議的方式。rest