ActionPush - ScutGame/Scut GitHub Wiki

此章节介绍服务端如何主动推送消息

消息推送概述

我们常用的是请求与响应模型(R-R),由客户端向服务器发起请求,并等待服务器响应;那么服务器能不有向客户端发起请求呢?答案是可以,但有条件,如果服务器是使用http协议是不支持服务器向客户端发起请求的,Socket和WebSocket协议的服务器有支持。

服务器推送消息

通过GameSession对象可以向指定玩家推送消息,只支持传字节数据,需要自己增加包头信息。

如何获取GameSession对象参考《服务端的Sesssion会话机制》。

示例1

推送一串文字给客户端,客户端不需要解包流程,实际网络传输有可能会被截断的情况。

static void Main()
{
    GameSession session = null;
    byte[] data = Encoding.UTF8.GetBytes("This is sent to the client data.");
    session.SendAsync(OpCode.Text, data, 0, data.Length, asyncResult =>
    {
        Console.WriteLine("The results of data send:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");

    });
}

示例2

推送一串文字给客户端,增加4个字节表示数据包的长度做为数据包头部,客户端需要判断收到的字节要大于等于数据包长度(数据包前4个字节转换为Int)。

static void Main()
{
    GameSession session = null;
    byte[] data = Encoding.UTF8.GetBytes("This is sent to the client data.");
    byte[] headBytes = BitConverter.GetBytes(data.Length);
    Byte[] buffer = new Byte[headBytes.Length + data.Length];
    Buffer.BlockCopy(headBytes, 0, buffer, 0, headBytes.Length);
    Buffer.BlockCopy(data, 0, buffer, headBytes.Length, data.Length);

    session.SendAsync(OpCode.Text, buffer, 0, buffer.Length, asyncResult =>
    {
        Console.WriteLine("The results of data send:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");

    });
}

示例3

采用Scut的二进制流推送给客户端, 它会创建长度+内容的头部包信息。

static void Main()
{
    GameSession session = null;
    MessageStructure ms = new MessageStructure();
    ms.WriteByte("This is sent to the client data.");
    byte[] buffer = ms.PopBuffer();
    session.SendAsync(OpCode.Text, buffer, 0, buffer.Length, asyncResult =>
    {
        Console.WriteLine("The results of data send:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");

    });
}

如果需要发送带有ActionId=1001结构的头部包信息,如下:

static void Main()
{
    GameSession session = null;
    MessageStructure ms = new MessageStructure();
    ms.PushIntoStack("This is sent to the client data.");
    //多条记录
    int count = 10;
    ms.PushIntoStack(count);
    for (int i = 0; i < count; i++)
    {
        var dsItem = new MessageStructure(); //子结构Record(同协议工具的Record结构)
        dsItem.PushIntoStack((int)i);
        dsItem.PushIntoStack("Name");
        dsItem.PushIntoStack(100); //分数
        ms.PushIntoStack(dsItem); //追加子结构
    }
    ms.WriteBuffer( new MessageHead(1001));
    byte[] buffer = ms.PopBuffer();
    session.SendAsync(OpCode.Text, buffer, 0, buffer.Length, asyncResult =>
    {
        Console.WriteLine("The results of data send:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");

    });
}

示例4

将一个自定义的包结构推送给客户端,需要用ProtoBufUtils 进行序列化和反序列化 , 在客户端用相应的 Action 来处理接收。

    [ProtoContract]
    public class ResponsePack
    {
        [ProtoMember(1)]
        public int MsgId { get; set; }

        [ProtoMember(2)]
        public int ActionId { get; set; }

        [ProtoMember(3)]
        public int ErrorCode { get; set; }
        [ProtoMember(4)]
        public string ErrorInfo { get; set; }

        [ProtoMember(5)]
        public string St { get; set; }
    }

static void Main()
{
     GameSession session = null;
     MyObject obj = new MyObject();
     byte[] head = ProtoBufUtils.Serialize(new ResponsePack() { ActionId = 1005 });
     byte[] body = ProtoBufUtils.Serialize(obj);

     byte[] headBytes = BitConverter.GetBytes(head.Length);
     byte[] buffer = new byte[headBytes.Length + head.Length + body.Length];

     Buffer.BlockCopy(headBytes, 0, buffer, 0, headBytes.Length);
     Buffer.BlockCopy(head, 0, buffer, headBytes.Length, head.Length);
     Buffer.BlockCopy(body, 0, buffer, headBytes.Length + head.Length, body.Length);

    session.SendAsync(OpCode.Text, buffer, 0, buffer.Length, asyncResult =>
    {
       Console.WriteLine("Push Action -> {0} result is -> {1}", actionId, result.Result == ResultCode.Success ? "ok" : "fail");
    });
}

服务器推送Action

如果想要同客户端请求服务器一样的方式来推送消息,服务器应该如何处理呢?

服务器可以模拟客户端创建一个HttpGet对象去调用指定的Action处理程序,传递的参数可以是Key-Value键值对和自定义对象的方式。

示例1

在请求的Action中触发,使用GameSession对象推送给客户端另一个Action处理程序的响应。

public class Action10001 : BaseAction
{
    
    public override void TakeActionAffter(bool state)
    {
        var parameters = new Parameters();
        parameters["ID"] = 123;
        var packet = ActionFactory.GetResponsePackage(1002, Current, parameters, httpGet.OpCode, null);
        ActionFactory.SendAction(Current, 1002, packet, (session, asyncResult) =>
        {
            Console.WriteLine("Action 1002 send result:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");
        
        }, 0);
        base.TakeActionAffter(state);
    }
}

示例2

在请求的Action中触发,使用GameSession对象向在线客户端推送另一个Action处理程序的响应。

public class Action10001 : BaseAction
{
    
    public override void TakeActionAffter(bool state)
    {
        var sessionList = GameSession.GetOnlineAll(10 * 1000);//心跳间隔10s发送
        var parameters = new Parameters();
        parameters["ID"] = 123;
        ActionFactory.SendAction(sessionList, 1002, parameters, (session, asyncResult) =>
        {
            Console.WriteLine("Action 1002 send result:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");

        }, httpGet.OpCode, 0);
        base.TakeActionAffter(state);
    }
}

示例3

在请求的Action中触发,使用IUser向客户端推送另一个Action处理程序的响应。

public class Action10001 : BaseAction
{
    
    public override void TakeActionAffter(bool state)
    {
        int usserId = 138000;
        var user = PersonalCacheStruct.Get<UserRole>(usserId.ToString());
        var userList = new List<IUser>();
        userList.Add(new SessionUser(user));
    
        var parameters = new Parameters();
        parameters["ID"] = 123;
        ActionFactory.SendAction(userList, 1002, parameters, (asyncResult) =>
        {
            Console.WriteLine("Action 1002 send result:{0}", asyncResult.Result == ResultCode.Success ? "ok" : "fail");
    
        }, httpGet.OpCode, 0);
        base.TakeActionAffter(state);
    }
}
⚠️ **GitHub.com Fallback** ⚠️