分享
  1. 首页
  2. 文章

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

懒皮 · · 1545 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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 学习笔记十四 反射

现在说一下执行顺序:

  • msg包里的init方法,执行Processor.Register把消息注册到map里(为什么先执行这一步我没搞懂)
  • game模块的handler.go里的init方法,使用skeleton.RegisterChanRPC把Function注册进去
  • gate模块的router.go里的init方法,使用msg.Processor.SetRouter(&msg.Test{}, game.ChanRPC)把消息转交给game模块处理

具体是怎么转交的,可以看一下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数据怎么处理

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1545 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏