<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] ## 緣由 最近在研究一款游戲的源碼。發現里面的通信協議是protobuf定義的,但還是自己定義了協議頭部,類似 head + body。先解析頭部里所存儲的整個包的數據長度,然后再解析包剩余的數據,這樣做的目的是為了防止沾包。我在想都用了protobuf了為啥不直接用grpc呢。一時想不出,于是我用golnag + protobuf 做了一個簡單的client + server ## 定義協議 一個包數據組成: * 包頭 * size :表示body長度,int32,占位4字節 * cmd : 表示指令,int32,占位 4字節 * body:表示protobuf數據。 ## .proto文件 ``` // msg.proto syntax = "proto3"; option go_package = ".;GCToLs"; package GCToLS; enum MsgID { eMsgToLSFromGC_Unknow = 0; eMsgToLSFromGC_Begin = 40960; eMsgToLSFromGC_AskLogin = 40961; eMsgToLSFromGC_End = 40970; } message AskLogin { MsgID msgid = 1; uint32 platform = 2; string uin = 3; string sessionid = 4; } ``` 執行命令,生成 msg.pb.go文件 ``` protoc ./*.proto --go_out=protos -I ./ ``` ## Server示例代碼 ``` package main import ( "bufio" "bytes" "encoding/binary" "fmt" "github.com/golang/protobuf/proto" pb "m1/protos" "net" ) func doServerStuf(conn net.Conn) { defer conn.Close() for { scanner := bufio.NewScanner(conn) scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { if !atEOF { if len(data) > 8 { //4字節數據包長度 4字節指令 length := int32(0) binary.Read(bytes.NewReader(data[0:4]),binary.BigEndian, &length) if length <= 0 { return 0, nil, fmt.Errorf("length is 0") } fmt.Printf("len_data %d; length: %d\n", len(data), length) if int(length) + 8 <= len(data) { return int(length), data[8:int(length) + 8], nil } } } return }) for scanner.Scan() { fmt.Println("scanner msg: ", string(scanner.Bytes())) msg := new(pb.AskLogin) err := proto.Unmarshal(scanner.Bytes(), msg) if err != nil { fmt.Printf("proto Unmarshal err: %s\n", err.Error()) return } fmt.Printf("msg.msgid: %d, msg.session: %s\n", msg.Msgid, msg.Sessionid) } if err := scanner.Err(); err != nil { fmt.Println("無數據包!") return } } } func main() { errs := make(chan error) l, err := net.Listen("tcp",":9091") if err != nil { errs <- err } fmt.Println("Accept...9001") go func() { conn ,err := l.Accept() if err != nil { errs <- err } go doServerStuf(conn) }() fmt.Errorf("%s\n", <- errs) } ``` ## Client示例代碼 ``` package main import ( "bytes" "encoding/binary" "fmt" "github.com/golang/protobuf/proto" pb "m1/protos" "net" ) func main() { conn, err := net.Dial("tcp","127.0.0.1:9091") if err != nil { fmt.Errorf("%s\n", err.Error()) return } msg := &pb.AskLogin{ Msgid: 1, Platform: 1, Uin: "1", Sessionid: "1001", } msgbyte,err := proto.Marshal(msg) if err != nil { fmt.Errorf("msg Marshal error %s\n", err.Error()) return } buf := &bytes.Buffer{} var head []byte head = make([]byte, 8) binary.BigEndian.PutUint32(head[0:4], uint32(bytes.Count(msgbyte,nil) -1)) binary.BigEndian.PutUint32(head[4:8], uint32(pb.MsgID_eMsgToLSFromGC_AskLogin)) buf.Write(head[:8]) buf.Write(msgbyte) fmt.Printf("%v\n", string(buf.Bytes())) conn.Write(buf.Bytes()) defer conn.Close() } ``` ## 知識點 一、golang的net網路庫的使用 二、golang的goroutine 三、golang的channel使用 四、bufio.Scan可以很好的分割一個數據包。 ### bufio.Scan的使用 ``` //生成一個 scanner scanner := bufio.NewScanner(conn) //自定義解包方法 scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {}) //獲取解析出的body for bufio.Scan{ fmt.printf("%s\n", string(scanner.Bytes())); } ``` 包頭是8字節,表示body長度的是 head[0:4]。表示指令的是head[4:8], 解析出body如下: ``` func doServerStuf(conn net.Conn) { defer conn.Close() for { scanner := bufio.NewScanner(conn) scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { if !atEOF { if len(data) > 8 { //4字節數據包長度 4字節指令 length := int32(0) binary.Read(bytes.NewReader(data[0:4]),binary.BigEndian, &length) if length <= 0 { return 0, nil, fmt.Errorf("length is 0") } fmt.Printf("len_data %d; length: %d\n", len(data), length) if int(length) + 8 <= len(data) { return int(length), data[8:int(length) + 8], nil } } } return }) for scanner.Scan() { fmt.Println("scanner msg: ", string(scanner.Bytes())) msg := new(pb.AskLogin) err := proto.Unmarshal(scanner.Bytes(), msg) if err != nil { fmt.Printf("proto Unmarshal err: %s\n", err.Error()) return } fmt.Printf("msg.msgid: %d, msg.session: %s\n", msg.Msgid, msg.Sessionid) } if err := scanner.Err(); err != nil { fmt.Println("無數據包!") return } } } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看