Firestone Architecture Design (7): using Google. ProtocolBuffers to process network messages,

Source: Internet
Author: User

Firestone Architecture Design (7): using Google. ProtocolBuffers to process network messages,

During this time, I thought about the network message processing in Unity3D online game development. The servers of online games are generally self-developed, so the corresponding network message processing should also be self-developed. Message transmission between the client and server uses JSON and Google. ProtocolBuffers. Open the hearth stone code and look at its processing method. I feel that the code is still well written. I will analyze its ideas and share it with you.

The overall Mechanism describes what we want to achieve:
  • There are N network messages, each of which corresponds to the message description in a Proto;
  • Each message corresponds to a digital ID;
  • The underlying layer parses a message into a Google. ProtocolBuffers. IMessage object. The specific type of the object should be the code generated by the previous message;
  • It is easy to send a message, because you know its type and can directly execute serialization;

Hearth stone uses Google. ProtocolBuffers class library, you can see here: http://www.nuget.org/packages/Google.ProtocolBuffers/

The mechanism for sending and sending messages is very simple. First, use the message class generated by ProtocolBuffer to construct a message object, for example, ConnectAPI. SendPing ()
public static void SendPing(){    Ping.Builder body = Ping.CreateBuilder();    QueueGamePacket(0x73, body);    s_lastGameServerPacketSentTime = DateTime.Now;}
The underlying layer constructs a "PegasusPacket" data packet object and adds it to the sending queue. This data packet object consists of three parts: Message ID, message size, and specific message data. For details, see the PegasusPacket. Encode () function:
public override byte[] Encode(){    if (!(this.Body is IMessageLite))    {        return null;    }    IMessageLite body = (IMessageLite) this.Body;    this.Size = body.SerializedSize;    byte[] destinationArray = new byte[8 + this.Size];    Array.Copy(BitConverter.GetBytes(this.Type), 0, destinationArray, 0, 4);    Array.Copy(BitConverter.GetBytes(this.Size), 0, destinationArray, 4, 4);    body.WriteTo(CodedOutputStream.CreateInstance(destinationArray, 8, this.Size));    return destinationArray;}
Next we will focus on the receipt and resolution of messages. First, because TCP is streaming, the bottom layer should detect the data packet header, collect a complete data packet, and then send it to the upper layer for resolution. This part of logic is in "ClientConnection <PacketType>. in BytesReceived. When a complete data packet is received, the OnPacketCompleted event is triggered in the main thread, and the ConnectAPI is actually called. packetReceived () ", which mainly calls the" ConnectAPI. queuePacketReceived () ", which resolves the byte [] received by the TCP layer into the corresponding IMessage object.
The point is coming! Because the packet sent from the network layer only contains one message ID, the client needs to solve the problem of finding the corresponding message Type from the ID. In our imagination, there are only two ways to do this: 1 is to manually record the Type corresponding to each ID; 2 is to create a class with an intermediate correspondence relationship, and attach the custom Attribute, then, the reflection mechanism is used to automatically collect these classes, which is similar to the former. Hearth stone adopts the first method. The overall mechanism is as follows:
  • Each message on the client corresponds to a derived class object of PacketDecoder;
  • The ConnectAPI class uses a dictionary to save the correspondence between <Message ID and Decoder Object>: ConnectAPI. s_packetDecoders: SortedDictionary <Int32, ConnectAPI. PacketDecoder>;
  • If a Decoder is required for each message and its internal code is completely consistent, isn't it a headache ?! Well, we use templates for implementation. For details, see the subsequent analysis;
  • When ConnectAPI. ConnectInit () is initialized, create a Decoder object and save it to the preceding dict, as shown in the following code:
    S_packetDecoders.Add (0x74, new DefaultProtobufPacketDecoder <Pong, Pong. Builder> ());
  • Finally, in the above function that receives the complete data packet, find the Decoder based on the Message ID recorded in the data packet, and then call its method to obtain the specific message object, similar to this:
         if (s_packetDecoders.TryGetValue(packet.Type, out decoder))    {        PegasusPacket item = decoder.HandlePacket(packet);        if (item != null)        {            queue.Enqueue(item);        }    }    else    {        Debug.LogError("Could not find a packet decoder for a packet of type " + packet.Type);    }
    Finally, let's take a look at the implementation skills of the Decoder template. First, the specific operation of Message Parsing is implemented by the code generated by Google. ProtocolBuffers, so the specific operation process is completely consistent. These are written to the static template functions of the base class:
    public abstract class PacketDecoder{    // Methods    public abstract PegasusPacket HandlePacket(PegasusPacket p);    public static PegasusPacket HandleProtoBuf<TMessage, TBuilder>(PegasusPacket p) where TMessage: IMessageLite<TMessage, TBuilder> where TBuilder: IBuilderLite<TMessage, TBuilder>, new()    {        byte[] body = (byte[]) p.Body;        TBuilder local2 = default(TBuilder);        TBuilder local = (local2 == null) ? Activator.CreateInstance<TBuilder>() : default(TBuilder);        p.Body = local.MergeFrom(body).Build();        return p;    }}
    Secondly, a template derived class is used to implement the virtual function HandlePacket (). The main purpose is to pass the TMessage and TBuilder types to the static function:
    public class DefaultProtobufPacketDecoder<TMessage, TBuilder> : ConnectAPI.PacketDecoder where TMessage: IMessageLite<TMessage, TBuilder> where TBuilder: IBuilderLite<TMessage, TBuilder>, new(){    // Methods    public override PegasusPacket HandlePacket(PegasusPacket p)    {        return ConnectAPI.PacketDecoder.HandleProtoBuf<TMessage, TBuilder>(p);    }}

    Okay. hearth stone uses ProtocolBuffers to process network messages. Is it clear!



    Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.