自定義協議和Golang實現

自定義協議和Golang實現

寫在這裏一是作一下總結在忘的時候方便查看,二是若是文章有錯誤請各位大佬噴我哈哈,歡迎指正。若是能幫到別人也挺好的。golang

協議

所謂協議就是指定一系列規則,這些規則使想要交流的雙方或多方能夠正常通訊交流。如咱們說的漢語就是協議,若是不按照漢語規則說話,交流的人就聽不懂對方說什麼,漢語和英語就是不一樣的協議,用漢語和只會英語的人交流,人家也聽不懂你說啥。在計算機中,入參出參是協議,最簡單的服務端rest接口是協議,系統實現一層層的網絡協議使計算機能夠互相通訊。網絡

自定義協議

自定義協議就是在現有協議知足不了咱們需求時,在現有協議之上構建的知足咱們本身程序的通訊需求的協議。目前的操做系統,會把咱們應用須要的底層網絡協議實現好,這對咱們來講是透明的,可是在咱們寫rest時,是否是須要定義接口呢,這接口就是咱們自定義的一種協議,經過定義請求參數,響應參數,讓客戶端和咱們寫好的服務端程序進行交互,這就是自定義的協議。socket

定義自定義協議

在TCP基礎上自定義協議,主要就是通訊雙方規定一個頭,頭後面是傳遞的數據。頭中規定一些參數,好比傳遞的消息前10個字節是頭,頭最前面2個字節是個固定魔數,讓你知道我是根據這個協議來通訊的,若是不支持這個協議的話,就能夠直接關掉這個鏈接了。跟着魔數的4個字節是整個包中數據的長度,這個長度不包括頭,須要長度的緣由是TCP協議面向的是流,會把應用本來一個一個發送的包一股腦的扔給對方,若是不用長度字段,咱們就頗有可能拿到了多個包的數據。最後4個字節是個校驗碼,校驗收到的包的數據是否有錯誤,主要是防止通訊時被篡改。
此時咱們就完成了咱們自定義協議的定義。只須要發送端在將數據寫入TCP前,在數據前面加入這個頭,接收端接收到數據時,按照這個頭的定義拿取數據,就能夠完成雙方的通訊了。函數

GO中定義協議

自定義了一個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

相關文章
相關標籤/搜索