Flag Counter
© 2017 Tao Peng. All rights reserved.

Go语言中使用 protobuf


2016年05月03日

protobuf以前只支持C++, Python和Java等语言, Go语言出来后, 作为亲儿子, 那有不支持的道理呢? github地址: go protobuf.

1. 安装protobuf

<1> 首先去 go protobuf 下载protobuf, 然后执行脚本./autogen.sh, 脚本中会检测需要的binary, 如果提示”XXX not found”, 那么安装就可以了. mac下使用brew, linux下不同发行版本使用自己的安装方法.

<2> 获取并安装proto-gen-go, go get github.com/golang/protobuf/protoc-gen-go, 这条命令会生成protoc-gen-go的可执行文件

<3> 注意将protoc-gen-go可执行文件路径加到PATH环境变量中, 或者将protoc-gen-go可执行文件放到类似于/usr/local/bin这样的路径下, 只要在PATH路径下就OK. 原因在于, protoc-gen-go可执行文件需要被protoc调用.

<4> 获取 goprotobuf 提供的支持库,包含诸如marshal、unmarshal等功能, 使用命令 go get github.com/golang/protobuf/proto.

<5> 写一个test.proto文件,执行protoc test.proto –go_out=.生成go语言的代码,如果提示libprotoc.so找不到,需要把/usr/local/lib 添加到LD_LIBRARY_PATH环境变量中。

2. 使用protobuf

首先我们需要写一个test.proto文件, 在这个文件中可以定义需要的结构, 例如枚举型, 结构体等等. 那么首先我自己定义了一个结构如下所示,

// test.proto
 package test;
 message myMsg
 {
    required int32     id = 1;   // ID
    required string    str = 2;  // str
    optional int32     opt = 3;  //optional field
 }

注意required是必须要求的字段, optional是可选字段. 同时注意, id=1, 后面的数字仅仅是一个unique标志而已, 保证唯一性就OK!
然后使用protoc test.proto –go_out=. 编译这个文件, 生成的文件名称为test.pb.go文件! 如果这个路径下有多个文件需要编译, 那么执行protoc –go_out=. *.proto就可以. 注意–go_out=后面的参数是生成的文件的路径, 本文生成的文件在’.’当前路径下.
生成的代码如下:

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

/*
Package test is a generated protocol buffer package.

It is generated from these files:
	1.proto

It has these top-level messages:
	MyMsg
*/
package test

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

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

type MyMsg struct {
	Id               *int32  `protobuf:"varint,1,req,name=id" json:"id,omitempty" bson:"id,omitempty"`
	Str              *string `protobuf:"bytes,2,req,name=str" json:"str,omitempty" bson:"str,omitempty"`
	Opt              *int32  `protobuf:"varint,3,opt,name=opt" json:"opt,omitempty" bson:"opt,omitempty"`
	XXX_unrecognized []byte  `json:"-"`
}

func (m *MyMsg) Reset()         { *m = MyMsg{} }
func (m *MyMsg) String() string { return proto.CompactTextString(m) }
func (*MyMsg) ProtoMessage()    {}

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

func (m *MyMsg) GetStr() string {
	if m != nil && m.Str != nil {
		return *m.Str
	}
	return ""
}

func (m *MyMsg) GetOpt() int32 {
	if m != nil && m.Opt != nil {
		return *m.Opt
	}
	return 0
}

func init() {
}

<font color=#0099ff>特别注意:</font> 生成的文件中的package是test, 那么文件必须放在test文件夹下! 否则会报错: “can’t load package: package test: found packages test (test.pb.go) and main (main.go)”


下面写一个测试程序:

// main.go
package main

import (
	"fmt"
	t "./test"
	"github.com/golang/protobuf/proto"
)

func main(){
    // 创建一个对象, 并填充字段, 可以使用proto中的类型函数来处理例如Int32(XXX)
	hw := t.MyMsg{
		Id: proto.Int32(1),
		Str: proto.String("iyviasbjasdv"),
		Opt: proto.Int32(2),

	}

    // 对数据进行编码, 注意参数是message指针
	mData, err := proto.Marshal(&hw)

	if err != nil {
		fmt.Println("Error1: ", err)
		return
	}

    // 下面进行解码, 注意参数
	var umData t.MyMsg
	err = proto.Unmarshal(mData, &umData)

	if err != nil {
		fmt.Println("Error2: ", err)
		return
	}

    // 输出结果
	fmt.Println(*umData.Id, "  ", *umData.Str, "  ", *umData.Opt)
}


简单的用法完毕, 然后我们就能愉快地使用protobuf定义自己的消息协议了, 赞!
只有protobuf中的编解码原理, 有时间再去深究了~~~

2. 参考

go protobuf
proto文档