In C #, struct is defined and converted into byte arrays,
Recently, the structure and byte array conversion were used in socket communication packet parsing. Because the client adopts C ++ development, the server adopts C # development, therefore, both parties must ensure that the types and lengths of the defined struct are consistent to ensure the correctness of message parsing. This is very important.
The first is struct definition. Some basic data types can be matched between C # And C ++:
[StructLayoutAttribute (LayoutKind. sequential, CharSet = CharSet. ansi, Pack = 1)] public struct Head {public ushort proMagic; // package start mark: Fixed 0x7e7e public ushort proPackLen; // package length: header + Data zone + package tail length, be sure not to exceed the maximum length limit public long proSrcAddr; // Source Address: not used, enter 0 public ushort proSrcPort; // source address port: not used, enter 0 public long proDstAddr; // target address: Not used; enter 0 public ushort proDstPort; // destination port: Not used; enter 0 public ushort pro1_code; // command code: see the above command code to define public ushort proVersion; // version: not used, fill in 1 public char proSerial; // message serial number: a message instance corresponds to a serial number, different packets are superimposed, 0-reciprocating public ushort proPackSum; // Total number of packages: when the package length exceeds the maximum length limit, the package needs to be split. The total number of large packages is not split. The default value is 1 public ushort proPackId; // current package number: identifies the number of packages listed above. The default value is 0 if no package is split}
1. The first is [StructLayoutAttribute (LayoutKind. sequential, CharSet = CharSet. ansi, Pack = 1)], which is a way for C # To reference an unmanaged C/C ++ DLL to define a struct, mainly for memory sorting, layoutKind has two attributes: Sequential and Explicit. Sequential indicates Sequential storage, and data in the Structure Body is stored in the memory sequentially. CharSet = CharSet. ansi indicates the encoding method. These are all prepared to use unmanaged pointers. You can remember these two points.
Note that the Pack = 1 featureIt represents the byte alignment of the struct. In actual development, the C ++ development environment starts to use the 2-byte alignment by default. Take the preceding packet header structure as an example, although the char type occupies one byte in the memory, when the struct is converted to a byte array, the system will automatically complete two bytes. Therefore, if C # is defined as Pack = 1, C ++ is two-byte aligned by default. If the structure of both parties is different in length, the conversion may be misplaced. Therefore, the default one-byte Alignment Method is required, C # defines Pack = 1, C ++ add # pragma pack 1 to ensure that the byte alignment in the struct is consistent.
2. For the definition of arrays, the length of each member in the struct must be clear, because the memory needs to allocate space based on this, and arrays in the C # struct cannot be initialized, here we need to define it in the member declaration;
/// <Summary> /// query terminal information /// </summary> [StructLayoutAttribute (LayoutKind. sequential, CharSet = CharSet. ansi, Pack = 1)] public struct PackTerminalSearch5001 {[financialas (UnmanagedType. byValTStr, SizeConst = 6)] // <summary> // terminal ID /// </summary> public string stationCode; [financialas (UnmanagedType. byValArray, SizeConst = 6)] /// <summary> /// reply command /// </summary> public Byte [] order ;} /// <summary> /// terminal information data /// </summary> [StructLayoutAttribute (LayoutKind. sequential, CharSet = CharSet. ansi, Pack = 1)] public struct PackTerminalSearch3004 {[financialas (UnmanagedType. byValTStr, SizeConst = 6)] /// <summary> /// terminal ID /// </summary> public string stationCode; /// <summary> /// Terminal IP /// </summary> public long terminalIP; /// <summary> /// terminal port /// </summary> public ushort terminalPort; /// <summary> /// central IP /// </summary> public long serverIP; /// <summary> /// test site port /// </summary> public ushort serverPort; /// <summary> /// disk information array /// </summary> [financialas (UnmanagedType. byValArray, SizeConst = 8)] public PackDiskInfo [] diskInfoArray;} // <summary> // disk information /// </summary> [StructLayoutAttribute (LayoutKind. sequential, CharSet = CharSet. ansi, Pack = 1)] public struct PackDiskInfo {// <summary> /// drive letter /// </summary> public char drive; /// <summary> /// total space /// </summary> public double totalSize; /// <summary> /// available space /// </summary> public double usableSize ;}
The code above should note that the string type is actually an array of Char [6] length. In actual use, only the first five characters can be used effectively, because the last character of char [6] Is \ 0 by default;
Iii. Conversion between struct and byte array
PackTerminalSearch5001 info; info.stationCode = "12345"; info.order = new byte[6] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; Byte[] recv = StructToBytes(info); object obj = BytesToStuct(recv, typeof(PackTerminalSearch5001)); PackTerminalSearch5001 info5001 = (PackTerminalSearch5001)obj; byte[] order = info5001.order;
//// <Summary> /// convert the struct to a byte array /// </summary> /// <param name = "structObj"> struct to be converted </param> /// <returns> converted byte array </returns> public static byte [] StructToBytes (object structObj) {// get the struct size int size = Marshal. sizeOf (structObj); // create a byte array byte [] bytes = new byte [size]; // allocate the memory space IntPtr structPtr = Marshal of the struct size. allocHGlobal (size); // copy the struct to the allocated memory. structureToPtr (structObj, structPtr, false); // copy data from memory space to byte array Marshal. copy (structPtr, bytes, 0, size); // release memory space Marshal. freeHGlobal (structPtr); // returns the byte array return bytes ;} /// <summary> /// convert the byte array to a struct. /// </summary> /// <param name = "bytes"> byte array </param> /// <param name = "type"> struct type </param> // <returns> converted struct </returns> public static object BytesToStuct (byte [] bytes, type type) {// obtain the int size of the struct = Marshal. sizeOf (type); // The length of the byte array is smaller than the size of the struct if (size> bytes. length) {// return null return;} // allocate the memory size of the struct IntPtr structPtr = Marshal. allocHGlobal (size); // copy the byte array to the allocated memory space Marshal. copy (bytes, 0, structPtr, size); // converts the memory space to the target object obj = Marshal. ptrToStructure (structPtr, type); // release memory space Marshal. freeHGlobal (structPtr); // return the struct return obj ;}