4

Imagine a schema:

namespace MyEvents;  

table EventAddress  
{  
  id:uint;  
  timestamp:ulong;  
  adress:string;  
}  

table EventSignalStrength  
{  
  id:uint;  
  timestamp:ulong; 
  strength:float;   
}  

table EventStatus  
{  
  status:string;  
}  

union Events {EventAddress, EventSignalStrength, EventStatus}  

table EventHolder  
{  
  theEvent:Events;  
}  

root_type EventHolder;  

For status message "EXIT", in C++ I encode and send over the wire like:

std::string message("EXIT");  
flatbuffers::FlatBufferBuilder builder;  
auto messageString= builder.CreateString(message);  // Message to send.  
auto statusEvent= MyEvents::CreateEventStatus(builder, messageString);  
auto eventHolder= MyEvents::CreateEventHolder(builder, MyEvents::Events_EventStatus, statusEvent.Union());  
builder.Finish(eventHolder);  

// Code to decode to check my work omitted, but the data decode properly in my real-world application.  

ret= sendto(m_udpSocket, reinterpret_cast<const char*>(builder.GetBufferPointer()), static_cast<int>(builder.GetSize()), 0, reinterpret_cast<SOCKADDR *>(&m_destination), sizeof(m_destination));  

For the same message, "EXIT", in C# I encode and send over the wire like:

string message= "EXIT";  
FlatBufferBuilder builder = new FlatBufferBuilder(1);  
StringOffset messageOffset = builder.CreateString(message);  
EventStatus.StartEventStatus(builder);  
EventStatus.AddStatus(builder, messageOffset);  
Offset<EventStatus> eventStatusOffset = EventStatus.EndEventStatus(builder);  

EventHolder.StartEventHolder(builder);  
EventHolder.AddTheEventType(builder, Events.EventStatus);  
EventHolder.AddTheEvent(builder, eventStatusOffset.Value);  
Offset<EventHolder> eventHolderOffset = EventHolder.EndEventHolder(builder);  

EventHolder.FinishEventHolderBuffer(builder, eventHolderOffset);  

// Test the encoding by decoding:  
EventHolder flatBuffer = EventHolder.GetRootAsEventHolder(builder.DataBuffer);  
Events flatBufferType = flatBuffer.TheEventType;  // Type looks good.  
EventStatus decodedEvent= new EventStatus();  
flatBuffer.GetDataObject<EventStatus>(decodedEvent);  // decodedEvent.Status looks good.  

// This code seems to send the correct data:  
Byte[] sendSized = builder.SizedByteArray();  
udpClient.Send(sendSized, sendSized.Length);  

// This code does not seem to send the correct data:  
//ByteBuffer sendByteBuffer = builder.DataBuffer;  
//udpClient.Send(sendByteBuffer.Data, sendByteBuffer.Data.Length);  

In my client application, written in C#, I decode as:

Byte[] receiveBytes = udpClient.Receive(ref m_remoteEndpoint);  
ByteBuffer flatBufferBytes= new ByteBuffer(receiveBytes);  
EventHolder flatBuffer = EventHolder.GetRootAsEventHolder(flatBufferBytes);  
Events flatBufferType= flatBuffer.DataObjectType;  
EventAddress eventAddress = null;  
EventSignalStrength eventSignalStrength = null;  
EventStatus eventStatus = null;  
switch (flatBufferType)  
{  
  case Events.EventAddress:  
  {  
    eventAddress = new EventAddress();  
    flatBuffer.GetDataObject<EventAddress>(eventAddress);  
    ProcessEventAddress(eventAddress);  
    break;  
  }  

  case Events.EventSignalStrength:  
  {  
    eventSignalStrength = new EventSignalStrength();  
    flatBuffer.GetDataObject<EventSignalStrength>(eventSignalStrength);  
    ProcessEventSignalStrength(eventSignalStrength);  
    break;  
  }  

  case Events.EventStatus:  
  {  
    eventStatus= new EventStatus();  
    flatBuffer.GetDataObject<EventStatus>(eventStatus);  
    Console.WriteLine("\nStatus Message: {0}", eventStatus.status);  
    break;  
  }  
}  
  • When I receive EventStatus messages from the C++ application, they decode properly.
  • When I receive EventStatus messages from the C# sending application, they decode properly.
  • When I dump the buffers sent from the applications, they are (in decimal):

  • C++ - 12 0 0 0 8 0 14 0 7 0 8 0 8 0 0 0 0 0 0 4 12 0 0 0 0 0 6 0 8 0 4 0 6 0 0 0 4 0 0 0 4 0 0 0 69 88 73 84 0 0 0 0

  • C# - 12 0 0 0 8 0 10 0 9 0 4 0 8 0 0 0 12 0 0 0 0 4 6 0 8 0 4 0 6 0 0 0 4 0 0 0 4 0 0 0 69 88 73 84 0 0 0 0

Originally, the messages from the C# sender were not decoding properly - now they are. I had made a change to the sender, so maybe had not rebuilt.

  • I am a little mystified that the received C++ buffer and the C# buffer are different, yet they decode properly to the same result.
  • My real-world schema is much more complex - am I following the proper procedure for decoding on the C# side?
  • Am I following the correct procedure for reducing the flatbuffer to Byte[] for sending over the wire in C#? It looks like I am, but it did not seem to work for awhile....

Any input appreciated.

GTAE86
  • 1,780
  • 3
  • 29
  • 39
  • I am having a similar issue and my buffer is not deserializing in C++ when serialized from C#. It can deserialize in C# though. May I know what change you made to make it work? Thanks! – Gengyu Shi Oct 28 '20 at 11:39
  • @GengyuShi - I'll try to take a look at my working code and see what I changed. – GTAE86 Nov 02 '20 at 22:05
  • @GengyuShi - I am pretty sure the above simple example is set up correctly. When I asked the question, the two things I was confused about were how to get the byte buffer to send and why the raw data blobs were different. I earlier had a problem with round-tripping the data, but had fixed it. My recollection is it just took a careful analysis of the data being encoded. Sounds like you are missing a step somewhere in your C++ - hard to know what without looking at your schema and code. My production code is working well for customers, and my C# sample client works well with C++ server. – GTAE86 Nov 12 '20 at 17:40
  • Thanks @GTAE86. My problem was partially related to additional C++ side verification. I used to have my C# producer having key type of long, which did not work. Changing it to byte[] and doing the byte conversion with BitConverter did the trick, although within C# it does not make a difference. – Gengyu Shi Nov 15 '20 at 01:28

1 Answers1

0

The ByteBuffer contains the buffer, but not necessarily at offset 0, so yes, turning it into a byte array (or sending the bytebuffer contents from its starting offset) are the only correct ways of sending it.

The encoding may differ between languages, as implementations may serialize things in different orders. Here, the C++ implementation decides to write the union type field before the offset, which happens to be inefficient for alignment, so it is a bit bigger. C# does the opposite.

Aardappel
  • 5,559
  • 1
  • 19
  • 22
  • So by "turning it into a byte array", you mean using FlatBufferBuilder method SizedByteArray()? To send "the bytebuffer contents from its starting offset" - that offset is Position, right? – GTAE86 Jun 08 '16 at 18:35