Hi there,

I was wondering if anyone could help me, I’m trying to send various packets of data over network via UDP. I’m using the sendto() function in winsock2.

I’m trying to send different data types over UDP, for example; I have positional data such as:

  int time;
  float LAT;
  double LONG;
  unsigned int ALT 

I have two problems:
How to package the data ready to send: as I understand it I have to reverse the byte order?
How to send the data using the sendto() that takes a const char pointer

This is what I’ve got so far:

// create a message to put all the data types into as I want send it in one lump    
  char message[20];

// reverse the byte order of everything
  int time = htonl(currentTime);
  float LAT = htonl((float)AC_Lat);
  double LONG = htonl(AC_Long);
  unsigned int ALT = htonl(AC_Alt);


// Copy it all to message
  memcpy(&message[0],&time, 4 );
  memcpy(&message[4],&LAT, 4 ); 
  memcpy(&message[8],&LONG, 8 );
  memcpy(&message[16],&ALT, 4 );

// I use this for testing purposes to check the contents of LAT, before the conversion it is 51.274999999, after the conversion it is 51!
  float tlat = 0;
  memcpy(&tlat,&message[4], 4 );
  tlat = htonl(tlat);

// this is my function that send it
  broadcastMessage(message,20);
  int time;
  float LAT;
  double LONG;
  unsigned int ALT 

I can send the data just fine but get garbage out the other end.

I would appreciate any help available; I’m fully open to a re-write as I think I’m barking up the wrong tree here!

Many Thanks

Here are a few facts that you need to know:

  1. Floating-point numbers are fixed by IEEE standards and do not differ between platform (except for extremely archaic ones). This means you don't need endianness conversions for float or double.
  2. Endianness conversions (e.g., htonl / ntohl) work on fixed-size unsigned integer types. The network conversion functions ending in l (for "long") use the integer type uint32_t, which is a fixed-size (32bit = 4bytes) unsigned integer type.
  3. The network byte-order conversion functions come in pairs. That is, htonl means "Host TO Network byte order for Long integers", and ntohl means "Network TO Host byte order for Long integers". You use htonl to prepare the value to be sent over, and you use ntohl to re-order the data that you have received from the network (to put it in proper order for the host platform).

So, you positional data should be of the following type:

  uint32_t time;
  float LAT;      // or: double LAT;
  double LONG;
  uint32_t ALT;

And before you send to the network, you need the following conversions:

// reverse the byte order of everything
  uint32_t time = htonl( uint32_t(currentTime) );
  uint32_t ALT  = htonl( uint32_t(AC_Alt) );
  // NOTE: the floating point values don't need conversion.

And, as a tip, a slightly nicer method for constructing the message is the following:

// Copy it all to message
  std::string message;
  message.append( (const char*) &time, sizeof(uint32_t));
  message.append( (const char*) &LAT,  sizeof(float));
  message.append( (const char*) &LONG, sizeof(double));
  message.append( (const char*) &ALT,  sizeof(uint32_t));

// this is my function that send it
  broadcastMessage(&message[0], message.size());

Also note that it is good practice to always use the sizeof operator, even for fixed-size types, it makes it clearer that a "4" or "8" isn't some magic number out of nowhere but the "size of the type", i.e., sizeof(uint32_t) or sizeof(double).

Hi Mike,

Many thanks for your detailed response, you’re an absolute star, it works a treat (I think).

I’m afraid I do have one more question….How does the data actually manifest itself in the message array?

Time for example, after being htonl’ed is 2490768209 and is an unsigned int.

Whilst debugging I see that it populate fields (all in decimal);

[0] = 80
[1] = 27
[2]= 118
[3] = -108

How do I relate this back to the htonl’ed time of 2490768209 ?

Many Thanks

Got it, convert to binary and back to decimal.

Cheers

Any Ideas how i would package the above in a structure and send that across UDP?

typedef struct UDPmessage
{
    uint32_t time;
    double LAT;
    double LONG;
    uint32_t ALT;

}Send_Out;

Send_Out.time = htonl( uint32_t(currentTime) )
Send_Out.ALT  = htonl( uint32_t(AC_Alt) );
Send_Out.LAT = AC_Lat
Send_Out.Long = AC_Long

How do send the structure?
Do i still need to cast each as a (const char*) ?
Do i have to create some sort of a buffer and fill it with the struct or can i just send the struct?
I'm told byte alignment's a thing!

Many Thanks

Unfortunately, byte alignment is an issue here. This means you cannot just send the structure, you have to fill in the individual members into a buffer. Byte alignment means that if, for example, you have a 64bit system with a native word size of 64bit (8 bytes), then the compiler might arrange you structure as so:

|     64bit     |     64bit     |     64bit     |     64bit     |
| 32bit | 32bit | 32bit | 32bit | 32bit | 32bit | 32bit | 32bit |
| time  |  pad  |      LAT      |      LONG     | ALT   |  pad  |

Where "pad" is for "padding", which are basically holes in the memory (with garbage data), which are there in order to keep data such as those double variables in proper alignment (not split up across two 64bit sections). On a 32bit architecture, you would probably not have this padding. And even on 64bit architectures, it might not even have the padding either (still have native 32bit word sizes). And, at the end of the day, it is also up to the compiler to decide what's the best alignment to use. In other words, you cannot really be sure of it. And code that tries to override the compiler's or platform's wishes in terms of alignment is going to be tedious to write and probably worse in performance.

So, I find that the easiest method to create a buffer for the data is to just use an std::string, like I showed in my previous post:

std::string message_buf;
message_buf.reserve( sizeof(Send_Out) );
message_buf.append( (const char*) &Send_Out.time, sizeof(uint32_t));
message_buf.append( (const char*) &Send_Out.LAT,  sizeof(double));
message_buf.append( (const char*) &Send_Out.LONG, sizeof(double));
message_buf.append( (const char*) &Send_Out.ALT,  sizeof(uint32_t));

broadcastMessage(&message_buf[0], message_buf.size());

You could also use a vector of chars, or a static array, or whatever is prefer, I just like the "append" style of std::string. And, of course, you have to do the same sort of thing at the receiving end.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.