Golang 游戏leaf系列(四) protobuf数据怎么处理

栏目: Go · 发布时间: 6年前

内容简介:在其实在game模块的handler.go中,同样注册了如何处理网络消息:也就是说,他们和内部模块通讯方式是一样的,也是用chanrpc。当然,我们还要做一些准备工作

Golang 游戏leaf系列(三) NewAgent在chanrpc和skeleton中怎么通讯 中(下文简称系列三),说了如何处理NewAgent:

skeleton.RegisterChanRPC("NewAgent", rpcNewAgent)

其实在game模块的handler.go中,同样注册了如何处理网络消息:

func init() {
    // 向当前模块(game 模块)注册 消息处理函数
    handler(&msg.Test{}, handleTest)
    handler(&msg.UserLogin{}, handleUserLogin)
    handler(&msg.UserRegister{}, handleUserRegister)
}

func handler(m interface{}, h interface{}) {
    skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
}

func handleTest(args []interface{}) {
    // 收到的 Test 消息
    m := args[0].(*msg.Test)
    // 消息的发送者
    a := args[1].(gate.Agent)

    // 输出收到的消息的内容
    log.Debug("hello %v", m.GetTest())

    retBuf := &msg.Test{
        Test: *proto.String("client"),
    }
    // 给发送者回应一个 Test 消息
    a.WriteMsg(retBuf)
}

也就是说,他们和内部模块通讯方式是一样的,也是用chanrpc。当然,我们还要做一些准备工作

一、流程简述

1.msg模块

// 使用 Protobuf 消息处理器
var Processor = protobuf.NewProcessor()

func init() {
    id1 := Processor.Register(&Test{})
    id2 := Processor.Register(&UserLogin{})
    id3 := Processor.Register(&UserRegister{})
    id4 := Processor.Register(&UserResult{})
    id5 := Processor.Register(&UserST{})
}

指定Processor并注册

2.在gate的router.go中设置路由

func init() {
    // 这里指定消息 路由到 game 模块
    msg.Processor.SetRouter(&msg.Test{}, game.ChanRPC)
    msg.Processor.SetRouter(&msg.UserLogin{}, game.ChanRPC)
    msg.Processor.SetRouter(&msg.UserRegister{}, game.ChanRPC)
}

3.简述

可以看到,三部分使用的都是 &msg.Test{} 这样的东西作为一个类似key的东西在互相沟通。

// -------------------------
// | id | protobuf message |
// -------------------------
type Processor struct {
    littleEndian bool
    msgInfo      []*MsgInfo
    msgID        map[reflect.Type]uint16
}

type MsgInfo struct {
    msgType       reflect.Type
    msgRouter     *chanrpc.Server
    msgHandler    MsgHandler
    msgRawHandler MsgHandler
}

type MsgHandler func([]interface{})

func (p *Processor) SetRouter(msg proto.Message, msgRouter *chanrpc.Server) {
    msgType := reflect.TypeOf(msg)
    id, ok := p.msgID[msgType]
    if !ok {
        log.Fatal("message %s not registered", msgType)
    }

    p.msgInfo[id].msgRouter = msgRouter
}

可以看到,路由的东西,确实是以 msgType := reflect.TypeOf(msg) 这种key存到了一个map里。这个map叫msgID,它的Value就是msgId。然后这些ID是从0开始的,作为访问切片msgInfo的索引。

关于msgId从0开始,在系列一讲过:

//leaf/network/protobuf.go的Register方法
    i := new(MsgInfo)
    i.msgType = msgType
    p.msgInfo = append(p.msgInfo, i)
    id := uint16(len(p.msgInfo) - 1)
    p.msgID[msgType] = id
    return id

关于使用 msgType := reflect.TypeOf(msg) 得到了一个 reflect.Type 作为key,会不会重复呢,因为msg是由自己定义的proto生成的结构,当然不会重复。但是为什么不用这个msg作为key,而是要用它的反射值呢,我没搞懂。关于反射,可以参考 Golang 学习笔记十四 反射

现在说一下执行顺序:

Processor.Register
skeleton.RegisterChanRPC
msg.Processor.SetRouter(&msg.Test{}, game.ChanRPC)

具体是怎么转交的,可以看一下protobuf的Router方法

func (p *Processor) Route(msg interface{}, userData interface{}) error {
    // raw
    if msgRaw, ok := msg.(MsgRaw); ok {
        if msgRaw.msgID >= uint16(len(p.msgInfo)) {
            return fmt.Errorf("message id %v not registered", msgRaw.msgID)
        }
        i := p.msgInfo[msgRaw.msgID]
        if i.msgRawHandler != nil {
            i.msgRawHandler([]interface{}{msgRaw.msgID, msgRaw.msgRawData, userData})
        }
        return nil
    }

    // protobuf
    msgType := reflect.TypeOf(msg)
    id, ok := p.msgID[msgType]
    if !ok {
        return fmt.Errorf("message %s not registered", msgType)
    }
    i := p.msgInfo[id]
    if i.msgHandler != nil {
        i.msgHandler([]interface{}{msg, userData})
    }
    if i.msgRouter != nil {
        i.msgRouter.Go(msgType, msg, userData)
    }
    return nil
}

可以看到是用 .Go(msgType, msg, userData) 这种方式。那么Route是什么时候调用的,这个在系列二看到过,大概就是收到消息时执行Run方法

func (a *agent) Run() {
    for {
        data, err := a.conn.ReadMsg()
        if err != nil {
            log.Debug("read message: %v", err)
            break
        }

        if a.gate.Processor != nil {
            msg, err := a.gate.Processor.Unmarshal(data)
            if err != nil {
                log.Debug("unmarshal message error: %v", err)
                break
            }
            err = a.gate.Processor.Route(msg, a)
            if err != nil {
                log.Debug("route message error: %v", err)
                break
            }
        }
    }
}

可以看到,Run中执行Route传入了两个参数,第一个是数据本身,第二个是agent。然后 .Go(msgType, msg, userData) 相当于把这两个参数都抛出去了。那么也可以知道我们在handler.go中的Function会如何处理:

func handleTest(args []interface{}) {
    // 收到的 Test 消息
    m := args[0].(*msg.Test)
    // 消息的发送者
    a := args[1].(gate.Agent)
...

第一个正是msg,第二个是Agent接口,因为agent没暴露,agent实现了Agent接口,所以暴露的是Agent接口。


以上所述就是小编给大家介绍的《Golang 游戏leaf系列(四) protobuf数据怎么处理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

赛博空间的奥德赛

赛博空间的奥德赛

(荷兰)约斯·德·穆尔 (Jos de Mul) / 麦永雄 / 广西师范大学出版社 / 2007-2 / 38.00元

本书揭示了数码信息时代的电子传媒与赛博空间为人类历史的发展提供的新的可能性。本书第一部分“通向未来的高速公路”,涉及无线想象、政治技术和极权主义在赛博空间的消解等题旨;第二部分“赛博空间的想象” ,讨论空间文学探索简史、电影和文化的数码化;第三部分”可能的世界” ,关涉世界观的信息化、数码复制时代的世界、数码此在等层面;第四、五部分探讨主页时代的身份、虚拟人类学、虚拟多神论、赛博空间的进化、超人文......一起来看看 《赛博空间的奥德赛》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试