[Go]Golang引入Protobuffer - Go语言中文社区

[Go]Golang引入Protobuffer


准备

在Golang中使用Protobuffer非常简单。

1.安装protobuffer的环境

你可以选择直接github上下载proto源码然后自行编译,这部分教程一大把不再赘述。
为了省心(偷懒),我打包了一份protobuffer2.6版本的release版,还包括给C#和Go使用的可执行程序。
连接在:protobuffer2.6下载
解压之后你会看到如下文件结构:
这里写图片描述

bin文件夹是protobuffer的realease目录:

这里写图片描述

其中:

  • CodeGenerator.exe 是编译.proto并生成对应的C#代码的程序
  • protoc-gen-go.exe 是编译.proto并生成对应的Go代码的程序

在主目录写了两个批处理文件:

//CreatePacket-CSharp.bat
binCodeGenerator.exe PBMessage.proto --output outputcsharp --preserve-names
//PBMessage.proto是输入的PB描述文件
//--ouput是命令参数 后边紧跟输出代码的目录
//--perserve-names是表示生成的代码使用自己在.proto描述的名字
//CreatePacket-Go.bat
binprotoc.exe --go_out=outputgo PBMessage.proto
//PBMessage.proto是输入的PB描述文件
//--go_out是命令参数 后边紧跟输出代码的目录

这里我的PBMessage.proto的描述如下:

package ProtobufPacket;

message CG_LOGIN_IN
{
    required int32 id = 1;
    required string account = 2;
    required string password = 3;
}

这里命名的package的名字是ProtobufPacket,对应的产生的go文件的packet名就是ProtobufferPacket,C#的命名空间是Protobuffer。

# 2.编译.proto文件

我们运行安装目录中的批处理文件,以go为例,我们得到名为PBMessage.pb.go的文件:

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: PBMessage.proto

/*
Package ProtobufPacket is a generated protocol buffer package.

It is generated from these files:
    PBMessage.proto

It has these top-level messages:
    CG_LOGIN_IN
*/
package ProtobufPacket

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

type CG_LOGIN_IN struct {
    Id               *int32  `protobuf:"varint,1,req,name=id" json:"id,omitempty"`
    Account          *string `protobuf:"bytes,2,req,name=account" json:"account,omitempty"`
    Password         *string `protobuf:"bytes,3,req,name=password" json:"password,omitempty"`
    XXX_unrecognized []byte  `json:"-"`
}

func (m *CG_LOGIN_IN) Reset()                    { *m = CG_LOGIN_IN{} }
func (m *CG_LOGIN_IN) String() string            { return proto.CompactTextString(m) }
func (*CG_LOGIN_IN) ProtoMessage()               {}
func (*CG_LOGIN_IN) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }

func (m *CG_LOGIN_IN) GetId() int32 {
    if m != nil && m.Id != nil {
        return *m.Id
    }
    return 0
}

func (m *CG_LOGIN_IN) GetAccount() string {
    if m != nil && m.Account != nil {
        return *m.Account
    }
    return ""
}

func (m *CG_LOGIN_IN) GetPassword() string {
    if m != nil && m.Password != nil {
        return *m.Password
    }
    return ""
}

func init() {
    proto.RegisterType((*CG_LOGIN_IN)(nil), "ProtobufPacket.CG_LOGIN_IN")
}

func init() { proto.RegisterFile("PBMessage.proto", fileDescriptor0) }

var fileDescriptor0 = []byte{
    // 115 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x0f, 0x70, 0xf2, 0x4d,
    0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x0b, 0x00, 0x51,
    0x49, 0xa5, 0x69, 0x01, 0x89, 0xc9, 0xd9, 0xa9, 0x25, 0x4a, 0x36, 0x5c, 0xdc, 0xce, 0xee, 0xf1,
    0x3e, 0xfe, 0xee, 0x9e, 0x7e, 0xf1, 0x9e, 0x7e, 0x42, 0x5c, 0x5c, 0x4c, 0x99, 0x29, 0x12, 0x8c,
    0x0a, 0x4c, 0x1a, 0xac, 0x42, 0xfc, 0x5c, 0xec, 0x89, 0xc9, 0xc9, 0xf9, 0xa5, 0x79, 0x25, 0x12,
    0x4c, 0x0a, 0x4c, 0x1a, 0x9c, 0x42, 0x02, 0x5c, 0x1c, 0x05, 0x89, 0xc5, 0xc5, 0xe5, 0xf9, 0x45,
    0x29, 0x12, 0xcc, 0x20, 0x11, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6d, 0x2b, 0xb1, 0x17, 0x5f,
    0x00, 0x00, 0x00,
}

把这个文件移动到你go项目的PBMessage文件夹下(或者你自己定义),不过此时我们的准备工作还没有完成。

在GOPATH目录执行命令:

go get github.com/golang/protobuf/proto

在你的项目的github目录下就会出现对应的支持protobuffer的go文件。

例子

这里我们举一个很简单的例子:

client:

    public void OnClickSend()
    {
        CG_LOGIN_IN pak = new CG_LOGIN_IN();
        pak.id = 1;
        pak.account = "ilovechina";
        pak.password = "root";
        byte[] bytes = CG_LOGIN_IN.SerializeToBytes(pak);
        m_Network.Send(bytes, 0, bytes.Length);
    }

在服务器:

package main

import (
    "net"
    "log"
    "fmt"
    "HttpServer/PBMessage"
    "github.com/golang/protobuf/proto"
)

const DefaultAddress = "localhost:8888"
const MaxByteCount = 1024 * 1024

func Welcome(conn net.Conn) {
    addr := conn.RemoteAddr()
    fmt.Printf("Welcome %vn", addr)
}

func ReadByte(conn net.Conn) {

    buffer := make([]byte, MaxByteCount)
    for{
        nbytes, err := conn.Read(buffer)
        if err != nil {
            log.Fatalln(err)
        }

        if nbytes > 0 {
            fmt.Println(nbytes, string(buffer))
            msg := ProtobufPacket.CG_LOGIN_IN{}
            err = proto.Unmarshal(buffer, &msg)//解码
            if err != nil {
                log.Fatalln(err)
            }
            fmt.Printf("id : %v  account : %v passwrod : %vn", *msg.Id, *msg.Account, *msg.Password)
        }
    }
}

func main() {

    listener, err := net.Listen("tcp", DefaultAddress)
    if err != nil {
        log.Fatalln(err)
    }

    for{
        connection, err := listener.Accept()
        if err != nil {
            log.Fatalln(err)
        }

        Welcome(connection)
        go ReadByte(connection)
    }

}

乍一看好像没有什么问题,但是如果你运行并且让服务器连接,你会发现服务器报错:
这里写图片描述
但是如果你无视这个错误,继续输出msg的值你会发现结果是对的,那么为什么会报这么一个错误呢?
原因在于切片buffer的长度,我们只需要解码的切片的长度应该是nbytes个,但是我们传给Unmarshal的buffer的长度是MaxBytesCount,所以这里只需要简单的修改为:

err = proto.Unmarshal(buffer[:nbytes], &msg)//仅解码我们需要的长度

这样运行就没有问题。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sixdaycoder/article/details/80535018
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-04 17:06:39
  • 阅读 ( 1038 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢