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");
});
}
如果想要同客户端请求服务器一样的方式来推送消息,服务器应该如何处理呢?
服务器可以模拟客户端创建一个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);
}
}