Parsing and data types problem...

bass20 used Ask the Experts™
...won't this hell ever end?

Is there any to "build" a UDP packet that can contain ints and chars? recvfrom() won't mind what kind of data it receives, that's up to the parsing, so can I create an array that will contain both these types? And this brings me another question; I'm parsing the received packets in the server side like this:

strncpy(oneInt,buffer,4); /*i copied a (char)int */
strncpy(string,&buffer[4],64); /*copied a 64B string*/

But how can I know if a char casted int will really take up 4B? Nevertheless, I need to send the array containing that int, so how can I retrieve it from the array later? I know I have to get 4B but how do I specify that in terms of positioning? Like this: myvar=buffer[68]; ??? I don't think this is right! :((
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2005

First, you cannot use functions (like strncpy) that treat the buffer as a NUL-teminated
ASCII string.  If your integer is a very small value (like 1), 3 of its four bytes will be 0,
which will be interpreted as "end of string" for all the string handling functions.  

If you want to store a 4 byte integer into the buffer you will have to use a cast like this:

oneInt = *(int *)buffer;

which means "force buffer to be a pointer to an integer (rather than a pointer to char),
then set oneInt to the value of the integer pointed to."


myvar = *(int *)(buffer+68);

which means "force the address of the 68th byte of buffer to be interpreted as a pointer
to an integer, then set to the value of the integer pointed to."

A packet will "send" anything that you put into it.  It's up to your client and server to be in agreement on the contents of the data.

This is why data to/from public servers is usually in ASCII (or UNICODE or some other portable mechanism).  "65536" is always "65536" when sent as a string of characters and the receiver is responsible for interrogating it.  Sending 65536 as a binary value can result in the receiver accessing the value is if it were 0, 1, 65536, or other values.

Try putting all data types you want to send in VARIANT type array and send that array instead.

for example, to send one integer and one character then you have to do the following:

     int nIntegerToSend = 1;
     char cCharToSend = 'a';

     VARIANT vArray[2];

     vArray[0].vt = VT_I4;
     vArray[0].lVal = (long)nIntegerToSend;

     vArray[0].vt = VT_UI1;
     vArray[0].bVal = (unsigned char)cCharToSend;

Now you have to send the buffer pointer to by vArray (length = sizeof(vArray)).

When you receive the buffer you will need to do something like this:

     int nBufferLength = /* Length of buffer received from socket (this has to be multible sizeof(VARIANT) */
     void *pBuffer = /*Start of Buffer received from socket*/

     void *pStart = pBuffer;
     VARIANT vt;
     while(1 == 1)
          memcpy(&vt, pBuffer, sizoef(VARIANT));
             case VT_I4:
                int nIntegerReceived = (int)vt.lVal;
             case VT_UI1:
                char cCharReceived = (char)vt.bVal;
              /* Handle other datatypes */
          /* Loop until you reach the end of buffer received */
         pBuffer += sizoef(VARIANT); /* make sure to handle buffer length not to get beyond the received buffer length :-) */
         if(pStart + nBufferLength <= pBuffer)

Should you be charging more for IT Services?

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Watch out for the int casting. If you're doing something like

char buffer[SIZE];
recvfrom(s, buffer, SIZE, ...);

You cannot safely cast a byte pointer to an int pointer. Some cpus aren't as forgiving wrt unaligned memory accesses as the x86.

As for the size of an int, you don't know whether its really four bytes or something else. Use some fixed-width type such as 'uint32_t' instead.

You'll also have to cater for byte-ordering differences between cpus by using a predetermined byte order when sending data over a network. Network byte order is big-endian.

Furthermore, it's probably a good idea to use some defensive programming when receiving UDP packets over a network. Imagine what would happen if someone sends you a malformed packet.

To avoid these headaches, better use a text encoding (as Kdo suggested). This is much less error-prone and easier to write.



So there isn't a way to use a type like void, wich will hold any kind of data? For ex:

void buffer[10];


I've posted a similar question before; I just can't quite figure out how to "blend" the two different types inside one array to use with sendto(). Should I convert all types to char?
The variant approach:

struct variant {
  char type;
  union {
    char c;
    short s;
    int i;
    long l;
    float f;
    double d;

Still has the same size/byte-order problems (not too mention overhead).

Maybe explicit encoding/decoding is a better idea:

typedef unsigned char uint8_t;

void uint_encode(uint8_t *buffer, unsigned int data)
      buffer[0] = (data >> 24) & 0xff;
      buffer[1] = (data >> 16) & 0xff;
      buffer[2] = (data >> 8) & 0xff;
      buffer[3] = data & 0xff;

unsigned int uint_decode(const uint8_t *buffer)
      int data;

      data = (unsigned int) buffer[0];
      data = (data << 8) | (unsigned int) buffer[1];
      data = (data << 8) | (unsigned int) buffer[2];
      data = (data << 8) | (unsigned int) buffer[3];
      return data;

Caters for size, byte-order and alignment problems.

The simplest way to interpret an incoming buffer of bytes as a struct is to just overlay the buffer with a struct pointer.

Something like:

char Buffer[1000];  // the incoming raw net data

typedef struct { int FirstField; double SecondField } TheNetPacket;  // how we want to look at the data

typedef  TheNetPacket *  TheNetPacketPtrType;

TheNetPacketPtrType   TP;

recv( Buffer );

TP = (TheNetPacketPtrType) Buffer;   // have TP point to the incoming data

printf("Received integrer '%d' and double '%f' \n", TP->FirstField, TP->SecondField );

But watch out, as others have noted, this will only work between compatible computers.
If it has to work between any two computers, they have to put the data into some mutually agreed format.

> So there isn't a way to use a type like void, wich will hold any kind of data? For ex:

> void buffer[10];

> strncopy(buffer,"hello",5);
> buffer[5]=123;

Yes, you can do something very similar to this.  But you must be very careful that the receiver knows the format well enough to decode the data.  Look what happens with slightly different data values:

main ()
  unsigned char Buffer[10];

  strcpy (Buffer, "hello");
  Buffer[5] = 44;
  Buffer[6] = 0;

  puts (Buffer);


Note that the decimal value of 44 becomes the period.  If the receiving field is expecting exactly 5 ascii characters and then an 8-bit value, no problem.  But if the text is "free form" as I've inferred, the receiver will have a monsterous time decoding the data.

> I've posted a similar question before; I just can't quite figure out how to "blend" the two different types inside one array to use with sendto(). Should I convert all types to char?

The variant approach that mtmike suggested will solve a lot of this.  Fixed length (fixed position) fields that can be described with struct{} will, too.  So will sending everything in ASCII (unicode).

Implementing variants is pretty easy.  For a first cut I would limit the data types to native types shown in mtmike's example.  Each item will be sizeof (variant) bytes long, but that's ok for starters.  Once the basics are in place for you to send/receive data you can then pack the data and include a string type.



The recipient of the packet always knows the format in order to parse it; here's how I'm doing it now, I'm about to start testing it:

char buffer[20];
int hey=3;

memcpy(buffer,(char *)hey,4);


Comments, please?

That should be fine.


Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial