1

I'm having some trouble with getting bytes of a C# struct sent over a TCP socket to a C++ client to read properly. Here are the structs...

Struct in C#

public struct TankBattleStateData
{
    public int playerID;
    public float currentHealth;

    public Vector3 position;
    public Vector3 forward;

    public Vector3 cannonForward;

    public int canFire;
    public int tacticalCount; 
}

Struct in C++

struct TankBattleStateData
{
    int playerID;
    float currentHealth;

    float position[3];
    float forward[3];

    float cannonForward[3];

    int canFire;
    int tacticalCount;
};

The transmission process is as follows...

  1. Use System.Runtime.InteropServices.Marshal to get an array of C# bytes representing the struct
  2. Feed array of bytes into Socket.Send to send to C++ client
  3. Client, upon receipt of bytes, does a memcpy of the bytes received to an unsigned char array on the heap, which is resized upon before copying if needed
    • There are some checks like verifying the number of bytes read, but nothing particularly robust in place.
  4. To read its contents, the client casts pointer to the char array as TankBattleStateData * and dereferences it to accesses its fields.

I find that consistently, the last field, tacticalCount is not the same value on the C++ client as it was on the C# client. For example, I'll send 1 from the server and get 81742424 back on client.

Environment

OS: Windows 10 x64
Server: C# w/ .NET Framework 2.0 (Unity3D 5.3.2f1)
Client: C++ w/ MSVC++14

What I'd Like To Know

  1. What am I doing wrong? ;)
    • More specifically, what am I doing wrong on the client? I set up a C# client and it seems to have no problem receiving the data, unlike the C++ client.
  2. Are there existing libraries that would make my life easier?

One hiccup I've had is that the C# bool is marshaled as 4 bytes, rather than 1 byte, which is the size of a bool in MSVC++14. I'd like to use a bool, but in an attempt to reduce the number of possible problems, I'm using an int for now.

Community
  • 1
  • 1
terrehbyte
  • 39
  • 8
  • What size are your C++ `int`s? Are you sure they aren't 16-bit? – SLaks Apr 17 '16 at 02:37
  • 1
    regarding (2), You can look into binary serialisation libraries like Google Protocol Buffers or MessagePack to do this for you in a platform-independent way. – yaakov Apr 17 '16 at 02:40
  • @SLaks While my problem has been resolved, my C++ `int`s were 4-bytes according to `sizeof(int)`, so that leads me to believe that they're 32-bit. (Thanks again!) – terrehbyte Apr 17 '16 at 03:29

2 Answers2

2

Add [StructLayout(LayoutKind.Sequential)] to the struct, then use [FieldOffset] to lay out the fields to exactly match the C++ version.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    Seems like I can only use [one or the other](https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.fieldoffsetattribute(v=vs.110).aspx). Using them both results in an error stating, `The FieldOffset attribute can only be placed on members of types marked with the StructLayout(LayoutKind.Explicit).` EDIT: Sorry, didn't realize hitting ENTER would submit my comment. Wasn't quite finished writing or testing out the two. Give me a second... – terrehbyte Apr 17 '16 at 02:44
  • 1
    @terrehbyte: You're right; I meant `Explicit`. (unless `Sequential` matches, in which case you can use that) – SLaks Apr 17 '16 at 13:38
2

What you should read about is serialization and deserialization of data structs. There are multiple protocols, libraries and methods available. Sending the data raw to/from memory can have strange implications regarding endian, text fields and arrays.

Stian Skjelstad
  • 2,277
  • 1
  • 9
  • 19