In depth and simple C-structure: an example of the structure of encapsulating Ethernet heartbeat packet


1. Application background

There are a large number of network messages (byte array) in the bottom equipment: heartbeat message, data acquisition message and alarm message reporting. A corresponding message structure is needed to parse these byte stream data.

2. Structure analysis

Therefore, I first thought of using structure to analyze. There are two reasons:

2.1. The structure exists in the stack

Class belongs to reference type and exists in the heap; structure belongs to value type and exists in the stack. When the main members of an object are data and the amount of data is small, using structure will bring better performance.

2.2. The structure does not need to be released manually

It belongs to the managed resource. The system automatically manages the life cycle. When the local method is called, it will be released automatically, and the global method will always exist.

3. Package heartbeat package structure

Heartbeat protocol message is as follows:

The corresponding structure is encapsulated as follows:

    [StructLayout(LayoutKind.Sequential, Pack = 1)] // Align by 1 byte
    public struct TcpHeartPacket

      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]   //Fixed length array in structure
      public byte[] head;

      public byte type;

      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
      public byte[] length;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
      public byte[] Mac;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 104)]
      public byte[] data;//Data volume
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
      public byte[] tail;

4. Structure static help class

It mainly realizes the conversion method from byte array to structure and from structure to byte array.

    public class StructHelper
        //// <summary>
        ///Structure to byte array
        /// </summary>
        ///< param name = "structobj" > structure to convert < / param >
        ///< returns > converted byte array < / returns >
        public static byte[] StructToBytes(Object structObj)
            //Get the size of the structure
            int size = Marshal.SizeOf(structObj);
            //Create byte array
            byte[] bytes = new byte[size];
            //Allocate structure size memory space
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //Copy the structure to the allocated memory space
            Marshal.StructureToPtr(structObj, structPtr, false);
            //Copy from memory space to byte array
            Marshal.Copy(structPtr, bytes, 0, size);
            //Free up memory
            //Return byte array
            return bytes;

        /// <summary>
        ///byte array to struct
        /// </summary>
        ///< param name = "bytes" > byte array < / param >
        ///< param name = "type" > structure type < / param >
        ///< returns > transformed structure < / returns >
        public static object BytesToStuct(byte[] bytes, Type type)
            //Get the size of the structure
            int size = Marshal.SizeOf(type);
            //byte array length is less than structure size
            if (size > bytes.Length)
                //Return empty
                return null;
            //Allocate structure size memory space
            IntPtr structPtr = Marshal.AllocHGlobal(size);
                //Copy byte array to allocated memory space
                Marshal.Copy(bytes, 0, structPtr, size);
                //Convert memory space to target structure
                return Marshal.PtrToStructure(structPtr, type);
                //Free up memory



Is the structure from 5.New in heap or stack?

Some colleagues said that all the new products would be put in the pile. I'm dubious. There are two ways to determine where to put the new structure. One is to use the debugging tools of Visual Studio to check, which has not been found for a long time. Passers-by experts please point out. The second way is to check the IL (Intermediate Language) language of decompilation dll. See how it's ultimately achieved. If you don't understand IL, you can see if you want to understand IL This article Article

5.1. Structure without formal parameters

  • Calling code
  //Initialize structure
  TcpHeartPacket tcpHeartPacket = new TcpHeartPacket();
  //Using the BytesToStuct method of StructHelper to convert byte stream into struct
  tcpHeartPacket = (TcpHeartPacket)StructHelper.BytesToStuct(ReceviveBuff, tcpHeartPacket.GetType());

From the corresponding IL code, it can be seen that it is only initobj and there is no newobj, where newobj means to allocate memory and complete object initialization, while initobj means to initialize the value type.

  • Newobj is used to allocate and initialize objects; initobj is used to initialize value types. Therefore, it can be said that newobj allocates memory in the heap and completes initialization; initobj initializes the memory already allocated on the stack, so the value type allocates memory on the stack during compilation.

  • newobj calls the constructor during initialization, while initobj does not call the constructor, but directly leaves the instance empty.

  • newobj has the process of memory allocation, while initobj only completes data initialization.

The execution result of initobj is to initially null the reference type in tcpHeartPacket, and set the primitive type to 0.
To sum up, the new structure (without parameters) is placed in the stack, only null/0 initialization is done.

5.2. Structure with parameters

Next, look at the storage location of the structure with parameters.
The simplified structure with parameters is as follows:

    public struct TcpHeartPacket

        public TcpHeartPacket(byte _type)
            type = _type;
        public byte type;


The call is as follows:

//new initialization with parameter structure
  TcpHeartPacket tcpHeartPacket = new TcpHeartPacket(0x1);
//Class new for comparison
  IWorkThread __workThread = new WorkThread();

The IL code is as follows:

In contrast, the new structure with parameters. IL just call s the ctor (constructor of the structure), and the new class below is newobj directly, which instantiates an object and stores it in the heap space.

Synthesis 5.1 and 5.2 shows that the new of the structure does exist in the stack, while the new of the class does exist in the stack.

6. Performance test

The test results are as follows:

It needs dozens of subtleties to use the structure parsing package, but the efficiency is still very poor. I use classes to package and parse. It only needs a few subtleties, and the performance is 5 to 10 times worse.

7. Cause analysis

The main time is spent in BytesToStuct method. See 4 for code details

  • Many byte [] byte arrays are used in the heartbeat package, and the byte array itself needs to open space in the heap;
  • The method has carried out the operation of packing and unpacking;
  • Whether the allocated memory is on the heap or the copy operation is performed on the heap;
    The IL code of the disassembly box is as follows:

The box instruction used for packing. Unboxing is the unbox.any instruction

8. Next phase: performance comparison test of heartbeat package between structure and class package

When the data is large, the data replication mechanism of structure will bring great cost. It's no wonder that one of the guidelines given by Microsoft is "do not use struct when the type definition is greater than 16 bytes". Finally, I also chose classes to encapsulate the parsing of Ethernet packets. The performance can reach the delicate level, which will be described in detail in the next article "heartbeat packet performance comparison test of structure and class encapsulation".

9. Use and share of IL tools

Copyright notice: This is the original article of the blogger, following CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint.

Link to this article:

Keywords: C# network Mac less

Added by MnM333 on Wed, 01 Apr 2020 23:41:55 +0300