package proto
在go代码中使用proto
import "github.com/golang/protobuf/proto"
后面通过类似proto.Marshall的方法来使用proto包。
至少需要使用到两个go的包,一个是proto编译出来的包,一个是proto包(主要用于序列化和反序列化)
proto文件:
syntax = "proto3";
option go_package="pbtest/";
message Test{
int32 i1=1;
enum NUMBER{
DEFAULT=0;
ACTIVATION=1;
SHUTDOWN=2;
}
NUMBER num=2;
}
client端:
package main
import (
"fmt"
"os"
pbtest "pblearn/pbtest"
"net"
"google.golang.org/protobuf/proto"
)
func main() {
conn, err := net.Dial("tcp4", "127.0.0.1:9528")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
defer conn.Close()
msg := pbtest.Test{}
fmt.Println(msg)
msg.I1 = 2
fmt.Println(msg)
msg.Num = 2
data, _ := proto.Marshal(&msg) //指针类型
conn.Write(data)
}
sever端:
package main
import (
"fmt"
"net"
"os"
pbtest "server/pbtest"
"google.golang.org/protobuf/proto"
)
func main() {
listener, err := net.Listen("tcp4", "127.0.0.1:9528")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
for {
conn, _ := listener.Accept()
defer conn.Close()
var buf [512]byte
n, _ := conn.Read(buf[0:])
var res pbtest.Test
proto.Unmarshal(buf[0:n], &res) //指针类型
fmt.Println(res)
}
}
protobuf编译出来的结构体以指针接收者方式都实现了proto.Message接口,例如Marshal和Unmarshal都用到了该类型的形参,所以.pb.go中的结构体需要传其指针类型给这些函数,才能进行序列化和反序列化。
func proto.Unmarshal(b []byte, m protoreflect.ProtoMessage) error
func proto.Marshal(m protoreflect.ProtoMessage) ([]byte, error)
编译出来的结构体都是以指针接收者的方式实现的各种方法,例如各种Getter和Setter。虽然指针对象和值对象都可以通过句号运算符访问这些方法,但是setter必须是指针对象,所以统一一下,都最好使用指针对象。
var buf [512]byte
n, _ := conn.Read(buf[0:])
var res pbtest.Test
proto.Unmarshal(buf[0:n], &res)
fmt.Println(res)
fmt.Println(res.GetNum()) //1
fmt.Println((&res).GetNum()) //2
//1和2都可以,但是如果是setter只能是2这种指针对象来进行
proto包的变量和api
变量Error
在使用proto包时,所有的错误都会赋给该变量,类似于errno一样的存在,该变量有方法Error(),能够获得具体的错误信息。
函数
func CheckInitialized(m Message) error
检查message对象是否已经初始化,若无则error非空
func Equal(x,y Message) bool
判断两个message对象是否相等,如果两个message对象序列化后的二进制数据相同,则该函数返回true
当两个message对象x和y都是无效的message时,Equal返回true,只有一个无效都返回false
x,y要相等,则必须保证所有已知的、扩展(extension)字段和值相同,且未知的(Unknwon)字段的值也要相同,才能返回true
标量类型(scalar type)使用==运算符进行判断,除了bytes类型使用底层的byte对比来判断。
Equal and floating point values which specially treat NaNs as equal. Message values are compared by recursively calling Equal. Lists are equal if each element value is also equal. Maps are equal if they have the same set of keys, where the pair of values for each key is also equal.
值相等的判断比较直观,如上英文所述。
func HasExtension(m Message, xt protoreflect.ExtensionType) bool
判断message对象是否有扩展
func ClearExtension(m Message,xt protoreflect.Extension)
去掉message对象中的extension字段,因此后续的HasExtension函数会返回false,如果m不是有效的message,或者xt没有扩展m,则调用时会产生panic
proto2中才有extension
func GetExtension(m Message, xt protoreflect.ExtensionType) interface{}
函数取得message对象m得extension字段的值,如果该字段没有设置,对于scalar类型返回默认值,对于message类型和列表类型返回空的不可修改的值。当xt没有扩展m时,函数引发panic
func SetExtension(m Message,xt protoreflect.ExtensionType, v interface{})
对m设置扩展字段的值,如果m无效,xt没有不是m的扩展类型,v不是xt对应的值类型,三者有一个出现就会引起panic
func RangeExtensions(m Message,f func(protoreflect.ExtensionType,interface {})bool )
该函数对m的扩展字段进行迭代(迭代顺序不确定),对每个扩展字段都调用函数f,只要当f返回了false,则本函数立刻返回。
func Marshal(m Message) []byte,error
最关键的函数之一,将message对象编码成二进制,并返回该byte切片。
func Unmarshal(b []byte,m Message) error
最关键的函数之一,将二进制的数据(byte切片)反序列化成message对象,传给m的实参必须是message对象的指针。
func Merge(dst, src Message)
Merge函数将源message合并到目标message中,必须保证dst和src是同一个message类型的实例。
源message中设置了的scalar字段会直接赋值给dst中的相应字段,如果是message类型的字段则会递归的调用Merge函数来实现合并。对于列表字段(例如repeated),则src中列表的值将append到dst的对应列表中;对于map类型的字段,将src的拷贝的dst中,因此可能会出现覆盖dst中map原有的某些key-value对(map的合并也相当于append,只不过重复的key会被src中的key覆盖掉)。src中未知的字段会被append到dst中的dst中的未知字段。
该函数的作用等价于在Unmarshal时指定UnmarshalOptions.Merge选项,指定该选项后,在对编码的源message反序列化到目标message对象时采用合并的逻辑。
func MessageName(m Message) protoreflect.FullName
返回message对象的类型名(.proto中定义的message名),如果m为nil,则返回空字符串
protoreflect.FullName该类型实现了String()方法,直接print就是message的名字。
func Reset(m Message)
重置清除message对象中的所有字段,对象m还在,只是字段全部清除了。
func Size(m Message) int
返回m编码后的二进制字节数
Bool, Int32, Int64, Uint32, Uint64, Float32, Float64, and String函数
这些函数都是为了方便给optional field的字段进行设置而给出的,proto2中的optional和proto3中的singular字段,在protoc编译后是结构体中的该字段类型的指针,而这些函数都是根据一个值,创建一个新的同类型对象,将值赋给该对象后返回其指针,这样方便用户对message对象的成员进行赋值。
函数的原型相似,下面仅给出Bool函数的解释,举一反三。
func Bool(v bool) *bool
传入一个bool值v,根据这个bool值分配新的空间,创建新的bool对象,赋为v,并返回其指针
proto包的类型
MarshalOptions类型
type MarshalOptions struct {
pragma.NoUnkeyedLiterals
// AllowPartial allows messages that have missing required fields to marshal
// without returning an error. If AllowPartial is false (the default),
// Marshal will return an error if there are any missing required fields.
// 设为true后,允许required字段缺失,并且在marshal时不返回错误,默认情况下是false
AllowPartial bool
// Deterministic controls whether the same message will always be
// serialized to the same bytes within the same binary.
//
// Setting this option guarantees that repeated serialization of
// the same message will return the same bytes, and that different
// processes of the same binary (which may be executing on different
// machines) will serialize equal messages to the same bytes.
// It has no effect on the resulting size of the encoded message compared
// to a non-deterministic marshal.
//
// Note that the deterministic serialization is NOT canonical across
// languages. It is not guaranteed to remain stable over time. It is
// unstable across different builds with schema changes due to unknown
// fields. Users who need canonical serialization (e.g., persistent
// storage in a canonical form, fingerprinting, etc.) must define
// their own canonicalization specification and implement their own
// serializer rather than relying on this API.
//
// If deterministic serialization is requested, map entries will be
// sorted by keys in lexographical order. This is an implementation
// detail and subject to change.
// 设为true后,使得同一个message编译后的二进制数据完全一致,存在一定的跨语言风险。
Deterministic bool
// UseCachedSize indicates that the result of a previous Size call
// may be reused.
//
// Setting this option asserts that:
//
// 1. Size has previously been called on this message with identical
// options (except for UseCachedSize itself).
//
// 2. The message and all its submessages have not changed in any
// way since the Size call.
//
// If either of these invariants is violated,
// the results are undefined and may include panics or corrupted output.
//
// Implementations MAY take this option into account to provide
// better performance, but there is no guarantee that they will do so.
// There is absolutely no guarantee that Size followed by Marshal with
// UseCachedSize set will perform equivalently to Marshal alone.
// 设为true后,复用上次对message调用Size函数计算的大小,但如果不满足1、2两点,则可能崩溃
UseCachedSize bool
}
MarshalOptions是一个结构体,proto包为该结构体提供了许多方法,以给出用这些选项对message进行编码的能力。
func (o MarshalOptions) Marshal(m Message) ([]byte,error)
使用MarshalOptions的选项来编码message,返回编码后的数据
func (o MarshalOptions) MarshalAppend(b []byte,m Message) ([]byte,error)
对m编码,并将编码后的二进制数据添加到字节切片b后,然后返回该字节切片。
func (o MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput,error)
返回message对象编码后的结果,该函数允许更细粒度的编码控制,一般用户用Marshal就行
func (o MarshalOptions) Size(m Message) int
返回m编码后的二进制字节大小
Message类型
type Message = protoreflect.ProtoMessage
这是一个接口类型,所有protoc编译出来的message结构体类型都以指针接收者的方式实现了该接口,所以这些对象的指针才能传给proto包的有关函数进行编解码等操作。
func Clone(m Message) Message
Clone函数返回一个m的深复制副本,相当于创建一个和m一模一样的message。
如果m是无效的,则会返回一个无效的message
UnmarshalOptions类型
type UnmarshalOptions struct {
pragma.NoUnkeyedLiterals
// Merge merges the input into the destination message.
// The default behavior is to always reset the message before unmarshaling,
// unless Merge is specified.
// 设为true后,解码的结果会和待写入的message对象进行合并
// 默认情况下是false,处理逻辑是先将待写入的message对象清空,再写入
Merge bool
// AllowPartial accepts input for messages that will result in missing
// required fields. If AllowPartial is false (the default), Unmarshal will
// return an error if there are any missing required fields.
// 设为true后,如果解码发现缺少required字段的信息,也不会报错
AllowPartial bool
// If DiscardUnknown is set, unknown fields are ignored.
// 设为true后,未知的字段信息都会丢弃
DiscardUnknown bool
// Resolver is used for looking up types when unmarshaling extension fields.
// If nil, this defaults to using protoregistry.GlobalTypes.
Resolver interface {
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
}
UnmarshalOptions是一个结构体,proto包为其提供了许多方法,以实现按照这些设置的行为进行解码的相关操作。
func(o UnmarshalOptions) Unmarshal(b []byte,m Message) error
对传入的二进制数据解析成message并写入到message中,
func(o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (potoiface.UnmarshalOutput, error)
对传入的二进制数据解析成message并写入到message中,该方法提供更细粒度的解析控制,但一般使用Unmarshal就行。