• C

Parsing and data types problem...

...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! :((
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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."

Kent OlsenDBACommented:

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)

Check Out How Miercom Evaluates Wi-Fi Security!

It's not just about Wi-Fi connectivity anymore. A wireless security breach can cost your business large amounts of time, trouble, and expense. Plus, hear first-hand from Miercom on how WatchGuard's Wi-Fi security stacks up against the competition plus a LIVE demo!

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.

bass20Author Commented:
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.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
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.

Kent OlsenDBACommented:
> 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.

bass20Author Commented:
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?
Kent OlsenDBACommented:

That should be fine.

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.