置顶 【必读】客户端如何收发mqant服务器的消息
发布于 1 年前 作者 liangdas 2508 次浏览 来自 分享

如何让客户端收发mqant服务器的消息,达到让客户端与mqant服务器双向通信的目的?这个问题可能目前困扰了很多同学,这里便做一下详细的讲解。

强烈建议查看mqant封装的JavaScript库,代码量很少 mqantlib.js

mqant通信基础

mqant使用的是目前非常流行的物联网通信协议mqtt协议,且只用到其中的数据收发功能,订阅功能没有用到。

通常mqtt客户端库都提供两个接口来实现消息的收发功能(mqantlib.js为例)

  1. 发送消息给服务端

    this.client.send(topic,payload ,qos);
    
    topic   字符串
    payload  body []byte 字节流
    qos   0
    
  2. 接收服务端发送来的消息

         this.client.onMessageArrived = onMessageArrived;  //注册消息接收处理事件
         function onMessageArrived(message) {
    	     message.topic 字符串
    		 message.payload  服务端发送过来的消息body  []byte
         }
    

以上就是mqant所用到的mqtt通信基础功能,目前mqtt各种编程语言各种平台都有对应的客户端开发库,这些开发库都支持以上的这两种接口,只可能各种语言上字段名称不同。但最重要的概念 topic和payload是不变的。

mqant对mqtt消息的扩展

以上的接口只是与服务端建立了双向通信,且能发送任何的数据。但在实际开发过程中我们会发送各种类型的数据给服务器,同时服务器也需要根据不同的数据类型做不同的处理。如何来区分这些数据呢?

mqant 使用topic来区分不同的消息类型,例如:

客户端给服务器发送消息的topic

     这里被makedown转义了,实际格式有点出入哈,没有那两个反斜杠

	 Shoot\@\Shoot001/HD_Enter    
	 代表访问mqant服务器的Shoot模块Shoot001进程的HD_Enter函数。作用是进入房间
	 Shoot\@\Shoot001/HD_Fire       
	 代表访问mqant服务器的Shoot模块Shoot001进程的HD_Fire函数。 作用是发射导弹
	 Shoot\@\Shoot001/HD_Move       
	 代表访问mqant服务器的Shoot模块Shoot001进程的HD_Move函数。 作用是移动位置	

发送这些消息给mqant服务器的前端代码表现为

		this.client.send("Shoot\@\Shoot001/HD_Move ",“{X:0,Y:100}” ,0);  //topic,payload

mqant服务器主动给客户端发送消息的topic

 Shoot/OnMove    有玩家移动的事件
 Shoot/OnFire       有玩家发射导弹的事件
 Shoot/OnJoin       有玩家加入房间的事件
 Shoot/OnSync     定时同步玩家状态的事件

发送这些消息在mqant服务器后端的代码表现为

 /**
通知玩家移动
 */
func (self *Table)NotifyOnMove(player *objects.Player){
	b, _ := json.Marshal(player.SerializableMap())
	player.Session().Send("Shoot/OnMove", b) //topic payload
	
}

客户端如何分发这些mqant发送过来的消息呢(重点)

目前mqantlib.js是这样处理的:

  1. 创建一个存储 topic与消息处理函数的哈希表 (Map)

    K:topic V:callback

  2. 当接收到mqant服务器发送过来的消息时通过topic查找callback 如果找到就调用这个callback,并将消息转发给这个callback

客户端收发消息的几种类型

  1. 发送消息—>需要等待mqant服务器对本次消息的处理结果
  2. 发送消息—>但不需要等待服务器的处理结果
  3. 接收mqant服务器主动下发给客户端的消息

mqant客户端中如何区分这些消息,并且正确分发给对应的callback呢?

mqant是这样处理的

针对以上几种消息收发情况,mqant对topic数据结构进行了约定和规范

客户端上行消息的topic格式

moduleType@moduleID/handler/msgid

moduleType	
    mqant后端模块名称 eg Chat  Login
moduleID	   
     后端模块的具体进程ID  因为一个模块可以被分配到多个进程运行,形成分布式集群,因此用moduleID来定位到具体的某一个进程中的模块
handle           
     实际的topic名称  例如:HD_Enter
msgid            
     标注这条消息的唯一性      这是一个可选项,但非常重要

通过以上的topic格式就可以定位到模块的具体一个进程的执行函数了

小贴示
 你也可以不指定moduleID   当不指定moduleID时 如果某地的模块有多个进程的话,mqant允许你在后端按一定的规则去控制选择将消息投递到哪一个进程中。类似于负载均衡
 eg.
   Shoot/HD_Enter  

如何发送消息并且等待服务器的处理结果

处理方式是 :topic加上msgid eg Shoot@Shoot001/HD_Enter/1200
其中1200 是客户端对消息的一个全局计数器,递增就行,保证唯一

当mqant服务器收到包含msgid格式的topic消息时它就知道这条消息需要返回处理结果。此时gate网关会将这条消息发送给后端模块后等待后端模块处理结果,当得到处理结果以后就会将结果发送给客户端。。

重点 mqant服务器返回的数据格式为 topic 保持与客户端上行的topic一致 也就是Shoot@Shoot001/HD_Enter/1200 payload 后端模块的处理结果

因此客户端收到这条消息的时候就可以通过 topic 确定是哪一次请求数据了,可以从本地的map中查找到对应的callback 然后调用。 一般情况下这种情况调用完成以后要讲callback 从map中删除,因为这是一次性的

如何只发送消息但是不需要服务器回复处理结果

解决方案: topic 不要msgid eg Shoot@Shoot001/HD_Enter

这种情况下gate模块是不会等待后端处理结果的,也不会回复客户端任何消息。

如何处理服务端主动下发的消息

一般情况下服务端主动给客户端发送的消息都是一些事件。例如用户加入房间,游戏开始,游戏暂停,游戏结束等。这些事件都可以定义成一个唯一的topic eg:

 Shoot/OnMove    有玩家移动的事件
 Shoot/OnFire       有玩家发射导弹的事件
 Shoot/OnJoin       有玩家加入房间的事件
 Shoot/OnSync     定时同步玩家状态的事件

客户端只需要在map中注册这些topic的callback就可以处理这些消息了。

最后给出我一个javascript的实际使用的代码段:

加入房间:

mqant.request(self.game.netServerId+"/HD_Enter",{
                "BigRoomId": self.game.bigRoomId,
            },function (data) {
                var message=JSON.parse(data.payloadString);
                if(message.Error!="") {
                    alert("加入房间失败,可能是该链接已失效");
                    location.href = "/qs/tacit";
                    return;
                }
                console.log(message);
            },this);

玩家移动

 var mqant=this.game.net.getNet();
        mqant.requestNR(this.game.netServerId+"/HD_Move",{
            Direction:direction
        });

接收服务端主动发送的事件

    mqant.on('Shoot/OnJoin', function(data) {
        var message=JSON.parse(data.payloadString);
        var SeatIndex=message.SeatIndex; //2 Active
        if(this.SeatIndex!=SeatIndex){
            alert("有玩家加入了游戏,可以点击开始按钮开始游戏了!")
        }
    },this);

    mqant.on('Shoot/OnSync', function(data) {

    },this);

   mqant.on('Shoot/OnStop', function(data) {
        this.OnStop(data);
    },this);

    mqant.on('Shoot/OnPause', function(data) {
        this.OnPause(data);
        this.game.paused = true;
    },this);
回到顶部