内容简介:在其实在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数据怎么处理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Python数据处理(二):处理 Excel 数据
- 自然语言处理之数据预处理
- Python数据处理(一):处理 JSON、XML、CSV 三种格式数据
- R|数据处理|因子型数据
- Pandas多维特征数据预处理及sklearn数据不均衡处理相关技术实践-大数据ML样本集案例实战
- 大数据 -- 下一代数据处理技术
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Google软件测试之道
James A. Whittaker、Jason Arbon、Jeff Carollo / 黄利、李中杰、薛明 / 人民邮电出版社 / 2013-10 / 59.00元
每天,google都要测试和发布数百万个源文件、亿万行的代码。数以亿计的构建动作会触发几百万次的自动化测试,并在好几十万个浏览器实例上执行。面对这些看似不可能完成的任务,谷歌是如何测试的呢? 《google软件测试之道》从内部视角告诉你这个世界上知名的互联网公司是如何应对21世纪软件测试的独特挑战的。《google软件测试之道》抓住了google做测试的本质,抓住了google测试这个时代最......一起来看看 《Google软件测试之道》 这本书的介绍吧!