Git Product home page Git Product logo

Comments (7)

bg5sbk avatar bg5sbk commented on June 27, 2024 2

大家好,我做了Protobuf的实验,基本上比较OK,具体应用到实际项目中可能需要再根据项目需求调整(比如根据包头来得知消息类型?)。

我用的库是这个:https://github.com/golang/protobuf

之前我没用过Protobuf,所以一开始花了点时间搭建环境,在Mac上搭建起来还是比较顺利的,就是要装上原始的Protobuf命令行工具,然后装上生成Go代码的Protobuf工具插件,插件必须让Protobuf工具可以搜索到,所以我把插件所在目录添加到PATH环境变量里。

然后就是按Protobuf项目给的Go示例代码,生成了一份test.pb.go代码出来,然后实现Protobuf的Codec。

因为Protobuf好像无法像Xml、Json、Gob这三个编码格式一样采用流编码和流解码,所以在Encoder和Decoder构造函数中必须强制要求外部传进来的是binary.PacketReaderbinary.PacketWriter,也就是说这个ProtobufCodec必须配合link.Packet()使用。(也可能是我对Protobuf的接口不熟悉,没找到流式编解码接口)

link的Codec实现是很灵活的,如果觉得一个个Codec套起来使用很烦,也可以直接就实现一个内置分包格式的项目专用Codec,这边只是一个简单示例,就不把各种可能性都列举一遍了。

下面是完整测试代码:

package main

import (
    "io"
    "sync"

    "github.com/funny/binary"
    "github.com/funny/link"
    "github.com/golang/protobuf/proto"
)

func main() {
    serverInitWait.Add(1)
    serverStopWait.Add(1)
    go server()
    go client()
    serverStopWait.Wait()
}

var (
    serverAddr     string
    serverInitWait sync.WaitGroup
    serverStopWait sync.WaitGroup
)

func server() {
    server, err := link.Serve("tcp", "0.0.0.0:0", link.Packet(link.Uint16BE, ProtobufCodec{}))
    if err != nil {
        panic(err)
    }
    serverAddr = server.Listener().Addr().String()
    serverInitWait.Done()

    session, err := server.Accept()
    if err != nil {
        panic(err)
    }

    newTest := &Test{}
    err = session.Receive(newTest)
    if err != nil {
        panic(err)
    }

    if newTest.GetLabel() != "hello" {
        println("data mismatch %q != 'hello'")
    } else {
        println("done")
    }

    session.Close()
    server.Stop()

    serverStopWait.Done()
}

func client() {
    serverInitWait.Wait()
    client, err := link.Connect("tcp", serverAddr, link.Packet(link.Uint16BE, ProtobufCodec{}))
    if err != nil {
        panic(err)
    }

    err = client.Send(&Test{
        Label: proto.String("hello"),
        Type:  proto.Int32(17),
        Optionalgroup: &Test_OptionalGroup{
            RequiredField: proto.String("good bye"),
        },
    })

    if err != nil {
        panic(err)
    }

    client.Close()
}

type ProtobufCodec struct {
}

func (_ ProtobufCodec) NewEncoder(w io.Writer) link.Encoder {
    return ProtobufEncoder{
        writer: w.(*binary.PacketWriter),
    }
}

func (_ ProtobufCodec) NewDecoder(r io.Reader) link.Decoder {
    return ProtobufDecoder{
        reader: r.(*binary.PacketReader),
    }
}

type ProtobufEncoder struct {
    writer io.Writer
}

func (pe ProtobufEncoder) Encode(msg interface{}) error {
    data, err := proto.Marshal(msg.(proto.Message))
    if err != nil {
        return err
    }
    _, err = pe.writer.Write(data)
    return err
}

type ProtobufDecoder struct {
    reader *binary.PacketReader
}

func (pe ProtobufDecoder) Decode(msg interface{}) error {
    data, err := pe.reader.ReadPacket()
    if err != nil {
        return err
    }
    return proto.Unmarshal(data, msg.(proto.Message))
}

这里一起给出test.pb.go的代码,方便大家实验:

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

/*
Package example is a generated protocol buffer package.

It is generated from these files:
    test.proto

It has these top-level messages:
    Test
*/
package main

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

type FOO int32

const (
    FOO_X FOO = 17
)

var FOO_name = map[int32]string{
    17: "X",
}
var FOO_value = map[string]int32{
    "X": 17,
}

func (x FOO) Enum() *FOO {
    p := new(FOO)
    *p = x
    return p
}
func (x FOO) String() string {
    return proto.EnumName(FOO_name, int32(x))
}
func (x *FOO) UnmarshalJSON(data []byte) error {
    value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO")
    if err != nil {
        return err
    }
    *x = FOO(value)
    return nil
}

type Test struct {
    Label            *string             `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
    Type             *int32              `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
    Reps             []int64             `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
    Optionalgroup    *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
    XXX_unrecognized []byte              `json:"-"`
}

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

const Default_Test_Type int32 = 77

func (m *Test) GetLabel() string {
    if m != nil && m.Label != nil {
        return *m.Label
    }
    return ""
}

func (m *Test) GetType() int32 {
    if m != nil && m.Type != nil {
        return *m.Type
    }
    return Default_Test_Type
}

func (m *Test) GetReps() []int64 {
    if m != nil {
        return m.Reps
    }
    return nil
}

func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
    if m != nil {
        return m.Optionalgroup
    }
    return nil
}

type Test_OptionalGroup struct {
    RequiredField    *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
    XXX_unrecognized []byte  `json:"-"`
}

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

func (m *Test_OptionalGroup) GetRequiredField() string {
    if m != nil && m.RequiredField != nil {
        return *m.RequiredField
    }
    return ""
}

func init() {
    proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
}

因为代码是两份,所以要用 go run main.go test.pb.go 这样的形式来运行测试代码。

from link.

bg5sbk avatar bg5sbk commented on June 27, 2024

可以的,跟序列化格式无关,跟分包协议也无关,参考已有的几个codec或利用已有的codec实现protobuf编解码器就可以了

from link.

oikomi avatar oikomi commented on June 27, 2024

@idada 达神有空可以实现一下:) 或者开一个目录等pr :)

from link.

chuangyou avatar chuangyou commented on June 27, 2024

.....这个自己实现不就好了。这么简单

from link.

oikomi avatar oikomi commented on June 27, 2024

赞达神 代码合到主线里吗

from link.

LaoZhongGu avatar LaoZhongGu commented on June 27, 2024

作为个例子来举例就行了,不要做大而全的东西。按需定制就可以了。

from link.

bg5sbk avatar bg5sbk commented on June 27, 2024

@oikomi 不适合放入项目里的,只是演示的代码,实际项目里用Protobuf应该要做更多事情的,比如上面说的消息类型识别,不同项目会很不一样。

from link.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.