ET中的ProtoBuf

基于大神的范例https://github.com/Maomao110/StarForce

GF做客户端,ET做服务端

通信流程

1、服务端启动,创建TCPService:

//Program.cs 添加外网消息组件
Game.Scene.AddComponent<NetOuterComponent, string>(outerConfig.Address);
//NetworkComponent.cs 创建TCPService
case NetworkProtocol.TCP:
	ipEndPoint = NetworkHelper.ToIPEndPoint(address);
	this.Service = new TService(packetSize, ipEndPoint, this.OnAccept) { Parent = this };

2、客户端连接

//ProcedureNetSample.cs 连接服务端
INetworkChannel nc = GameEntry.Network.CreateNetworkChannel("TC", netHelper);
nc.HeartBeatInterval = 0f;
nc.Connect(ip, 10002);

3、服务端收到客户端连接请求,创建TcpChannel

//TService.cs OnAcceptComplete
TChannel channel = new TChannel(e.AcceptSocket, this);

4、客户端连接成功回调,发送消息

//ProcedureNetSample.cs
channel.Send(new C2R_Login() { Account = "mdzz", Password = "zzdm" });

//NetworkManager.NetworkChannelBase.cs 
//Send<T>(T packet)
m_SendPacketPool.Enqueue(packet);

//ProcessSend() 序列化消息
serializeResult = m_NetworkChannelHelper.Serialize(packet, m_SendState.Stream);

 序列化消息的核心代码

public bool Serialize<T>(T packet,Stream destination) where T : Packet
{
...
    //消息主体
    memoryStream.Seek(PacketHeaderLength, SeekOrigin.Begin);
    memoryStream.SetLength(PacketHeaderLength);
    //写入消息
    ProtobufHelper.ToStream(packet, memoryStream);

    // 头部消息
    ET_CSPacketHeader packetHeader = ReferencePool.Acquire<ET_CSPacketHeader>();
    packetHeader.Flag = 0;      //客户端发送的消息,默认flag为0,服务器会解析flag字段值
    packetHeader.PacketLength = (int)memoryStream.Length - ETPackets.ET_PacketSizeLength; // 消息内容长度需要减去头部消息长度,只包含packetSize一个字段
    packetHeader.Id = (ushort)packet.Id;
    
    //写入头部消息
    memoryStream.Position = 0;
    this.byteses[0].WriteTo(0, (ushort)packetHeader.PacketLength);
    this.byteses[1][0] = packetHeader.Flag;
    this.byteses[2].WriteTo(0, packetHeader.Id);
    int index = 0;
    foreach(var bytes in this.byteses)
    {
        Array.Copy(bytes, 0, memoryStream.GetBuffer(), index, bytes.Length);
        index += bytes.Length;
    }
...
}

ProtobufHelper.ToStream(packet, memoryStream);调用对应消息的WriteTo

public void WriteTo(pb::CodedOutputStream output) {
    if (Account.Length != 0) {
    output.WriteRawTag(10);
    output.WriteString(Account);
    }
    if (Password.Length != 0) {
    output.WriteRawTag(18);
    output.WriteString(Password);
    }
    if (RpcId != 0) {
    output.WriteRawTag(208, 5);
    output.WriteInt32(RpcId);
    }
}

消息的全部字节信息如下:

1(第一个字节,下同)、2:消息长度,序列化全部消息后,可得出此字节的值

3:Flag = 0 => 0

4、5:消息ID,对应服务端Opcode,当前消息ID =10001 

 10001 = 00100111 00010001= 00100111(39) | 00010001(17) => 17,39(即39x256+17=10001)

6:Account FieldNumer = 1,WireType = 2

(field_number << 3) | wire_type = 00001010 => 10

7:Account = "mdzz",长度为4 => 4

8、9、10、11:Account = "mdzz",查询ASCII表,m、d、z、z => 109、100、122、122

12:Password FieldNumer = 2,WireType = 2 => 00010010 => 18

13:Password = "zzdm",长度为4 => 4

14、15、16、17:Password = "zzdm",查询ASCII表,z、z、d、m => 122、122、100、109

RpcId = 0,消息全部获取完毕,消息的字节数为17,不包含表示消息长度的两个字节,所以还要扣除两个字节,因此第1、2字节 => 15,0

根据以上计算得出消息的字节信息为

15,0,17,39,10,4,109,100,122,122,18,4,122,122,100,109

与实际结果相同

5、服务端接收消息

//TChannel.cs
//OnRecvComplete
//解析消息
this.parser.Parse()
...
//session回调 ReadCallback
this.OnRead(this.parser.GetPacket());

Parse()先获取消息长度,再将把除字节长度以外的消息写入,所以只有15个字节

在创建TcpChannel,同时也创建了Seesion,TcpChannel的ReadCallback调用Session.cs的OnRead() 、Run()

//NetworkComponent.cs
public void OnAccept(AChannel channel)
{
    Session session = ComponentFactory.CreateWithParent<Session, AChannel>(this, channel);
    this.sessions.Add(session.Id, session);
    session.Start();
}

//Session.cs
public void Awake(AChannel aChannel)
{
	this.channel = aChannel;
	this.requestCallback.Clear();
	long id = this.Id;
	channel.ErrorCallback += (c, e) =>
	{
		this.Network.Remove(id); 
	};
	channel.ReadCallback += this.OnRead;
}

6、服务端解析消息

//Session.cs
//OnRead() Run()
message = this.Network.MessagePacker.DeserializeFrom(instance, memoryStream);

//ProtobufHelper.cs
((Google.Protobuf.IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length);

最终会调用对应消息的MergeFrom

public void MergeFrom(pb::CodedInputStream input) {
      account_ = "";
      password_ = "";
      rpcId_ = 0;
      uint tag;
      while ((tag = input.ReadTag()) != 0) {
        switch(tag) {
          default:
            input.SkipLastField();
            break;
          case 10: {
            Account = input.ReadString();
            break;
          }
          case 18: {
            Password = input.ReadString();
            break;
          }
          case 720: {
            RpcId = input.ReadInt32();
            break;
          }
        }
      }
    }

7、返回服务端R2C_XXX消息

//Session.cs
if ((flag & 0x01) == 0)
{
	this.Network.MessageDispatcher.Dispatch(this, opcode, message);
	return;
}

//OuterMessageDispatcher.cs
Game.Scene.GetComponent<MessageDispatcherComponent>().Handle(session, new MessageInfo(opcode, message));

//AMRpcHandler.cs
//Handler()
this.Run(session, request, response =>
{
	// 等回调回来,session可以已经断开了,所以需要判断session InstanceId是否一样
	if (session.InstanceId != instanceId)
	{
		return;
	}

	response.RpcId = rpcId;
	session.Reply(response);
});

//C2R_LoginHandler.cs
private async ETVoid RunAsync(Session session, C2R_Login message, Action<R2C_Login> reply)
{
//创建R2C_XXX消息
    R2C_Login response = new R2C_Login();
...
    response.Address = outerAddress;
    response.Key = g2RGetLoginKey.Key;
    reply(response);
}
//Session.cs
public void Send(byte flag, ushort opcode, object message)
{
...
//序列化R2C_XXX
	MemoryStream stream = this.Stream;

    stream.Seek(Packet.MessageIndex, SeekOrigin.Begin);
	stream.SetLength(Packet.MessageIndex);

    this.Network.MessagePacker.SerializeTo(message, stream);
	stream.Seek(0, SeekOrigin.Begin);
	
	this.byteses[0][0] = flag;
	this.byteses[1].WriteTo(0, opcode);
	int index = 0;
	foreach (var bytes in this.byteses)
	{
		Array.Copy(bytes, 0, stream.GetBuffer(), index, bytes.Length);
		index += bytes.Length;
	}
...
//发送消息
	this.Send(stream);
}
//TChannel.cs
//加上头部的2个字节,表示消息的字节长度
Send()

8、客户端接收R2C_XXX消息

//NetworkManager.TcpNetworkChannel
//ReceiveCallback()
//解析包头、包体
if (m_ReceiveState.PacketHeader != null)
{
    processSuccess = ProcessPacket();
}
else
{
    processSuccess = ProcessPacketHeader();
}

//NetworkManager.NetworkChannelBase.cs
...
Packet packet = m_NetworkChannelHelper.DeserializePacket(m_ReceiveState.PacketHeader, m_ReceiveState.Stream, out customErrorData);
...
IPacketHeader packetHeader = m_NetworkChannelHelper.DeserializePacketHeader(m_ReceiveState.Stream, out customErrorData);
...

解析包体,调用对应消息的MergeFrom

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值