golang二進位協議介面映射

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

在寫伺服器程式時,特別是業務向的服務(比如遊戲伺服器),經常會遇到處理許多用戶端協議的情況,如果是http服務,那麼定義好處理介面,剩下的交給web伺服器就可以了。但是二進位協議就沒有這麼方便了。

通常的自訂二進位協議規則都是固定長度訊息頭+變長訊息體構成,在訊息頭中會有訊息長度,訊息id等欄位。(基於TCP流式協議),伺服器接收到用戶端訊息後,首先讀取訊息頭,解析得到訊息長度,再按照指定長度擷取到完整的訊息體的位元據。

在寫具體商務邏輯時,需要面臨從網路層擷取到的未經處理資料,怎麼映射到記憶體資料結構並調用相應處理介面的問題。前面所說的二進位訊息體的格式多種多樣,大家都有自己的做法,這裡以protobuf為例,構建伺服器端接收到未經處理資料後,通過訊息id映射產生對應的protobuf結構體,並調用處理介面。

golang種有一個reflect包,可以對類型進行反射,動態產生相應結構體,具體做法就是,將protobuf訊息結構通過interface類型和訊息id註冊到一個自訂map中,在map中儲存結構體的類型,具體如下:

type MessageHandler func(msgid uint16, msg interface{})type MessageInfo struct {    msgType    reflect.Type    msgHandler MessageHandler}var (    msg_map = make(map[uint16]MessageInfo))func RegisterMessage(msgid uint16, msg interface{}, handler MessageHandler) {    var info MessageInfo    info.msgType = reflect.TypeOf(msg.(proto.Message))    info.msgHandler = handler    msg_map[msgid] = info}

然後從底層網路擷取到原始二進位協議資料後,通過訊息id在map中找到對應的類型資訊並動態建立出結構體類型來解析位元據,具體如下:

func HandleRawData(msgid uint16, data []byte) error {    if info, ok := msg_map[msgid]; ok {        msg := reflect.New(info.msgType.Elem()).Interface()        err := proto.Unmarshal(data, msg.(proto.Message))        if err != nil {            return err        }        info.msgHandler(msgid, msg)        return err    }    return errors.New("not found msgid")}

這裡利用了reflect的反射機制,動態擷取類型並建立了protobuf結構體,然後通過proto.Umarshal介面解析二進位訊息體,最後調用msgHandler進行處理。這裡的msgHandler是一個訊息處理介面類型,每個訊息都按照規範定義自己的處理函數,在程式啟動的時候將訊息,protobuf結構體和處理函數都統一註冊,如下:

const (    MsgID_Test1 = iota    MsgID_Test2)func MessageHandler_Test1(msgid uint16, msg interface{}) {    p := msg.(*pb.MsgTest1)    fmt.Println("message handler msgid:", msgid, " body:", p)}func MessageHandler_Test2(msgid uint16, msg interface{}) {    p := msg.(*pb.MsgTest2)    fmt.Println("message handler msgid:", msgid, " body:", p)}func RegistMsg() {    RegisterMessage(MsgID_Test1, &pb.MsgTest1{}, MessageHandler_Test1)    RegisterMessage(MsgID_Test2, &pb.MsgTest2{}, MessageHandler_Test2)}

此處註冊函數使用的是protobuf訊息體的指標類型,所以reflect類型反射的時候,需要通過類型的Elem()函數得到指標的基底類型,再動態建立類型。

這樣處理之後,每次新增協議只需要在RegistMsg函數裡面新加一行即可,不需要每個協議再單獨處理二進位協議轉換,結構體映射等重複而繁雜的事情。

代碼在: https://github.com/pertgame/gmsg-framework

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.