Passing a structure through Sockets in C

93,222

Solution 1

This is a very bad idea. Binary data should always be sent in a way that:

Don't ever write a whole struct in a binary way, not to a file, not to a socket.

Always write each field separately, and read them the same way.

You need to have functions like

unsigned char * serialize_int(unsigned char *buffer, int value)
{
  /* Write big-endian int value into buffer; assumes 32-bit int and 8-bit char. */
  buffer[0] = value >> 24;
  buffer[1] = value >> 16;
  buffer[2] = value >> 8;
  buffer[3] = value;
  return buffer + 4;
}

unsigned char * serialize_char(unsigned char *buffer, char value)
{
  buffer[0] = value;
  return buffer + 1;
}

unsigned char * serialize_temp(unsigned char *buffer, struct temp *value)
{
  buffer = serialize_int(buffer, value->a);
  buffer = serialize_char(buffer, value->b);
  return buffer;
}

unsigned char * deserialize_int(unsigned char *buffer, int *value);

Or the equivalent, there are of course several ways to set this up with regards to buffer management and so on. Then you need to do the higher-level functions that serialize/deserialize entire structs.

This assumes serializing is done to/from buffers, which means the serialization doesn't need to know if the final destination is a file or a socket. It also means you pay some memory overhead, but it's generally a good design for performance reasons (you don't want to do a write() of each value to the socket).

Once you have the above, here's how you could serialize and transmit a structure instance:

int send_temp(int socket, const struct sockaddr *dest, socklen_t dlen,
              const struct temp *temp)
{
  unsigned char buffer[32], *ptr;

  ptr = serialize_temp(buffer, temp);
  return sendto(socket, buffer, ptr - buffer, 0, dest, dlen) == ptr - buffer;
}

A few points to note about the above:

  • The struct to send is first serialized, field by field, into buffer.
  • The serialization routine returns a pointer to the next free byte in the buffer, which we use to compute how many bytes it serialized to
  • Obviously my example serialization routines don't protect against buffer overflow.
  • Return value is 1 if the sendto() call succeeded, else it will be 0.

Solution 2

There is no need to write own serialisation routines for short and long integer types - use htons()/htonl() POSIX functions.

Solution 3

If you don't want to write the serialisation code yourself, find a proper serialisation framework, and use that.

Maybe Google's protocol buffers would be possible?

Solution 4

Serialization is a good idea. You can also use Wireshark to monitor the traffic and understand what is actually passed in the packets.

Share:
93,222
codingfreak
Author by

codingfreak

Updated on July 09, 2022

Comments

  • codingfreak
    codingfreak almost 2 years

    I am trying to pass whole structure from client to server or vice-versa. Let us assume my structure as follows

    struct temp {
      int a;
      char b;
    }
    

    I am using sendto and sending the address of the structure variable and receiving it on the other side using the recvfrom function. But I am not able to get the original data sent on the receiving end. In sendto function I am saving the received data into variable of type struct temp.

    n = sendto(sock, &pkt, sizeof(struct temp), 0, &server, length);
    n = recvfrom(sock, &pkt, sizeof(struct temp), 0, (struct sockaddr *)&from,&fromlen);
    

    Where pkt is the variable of type struct temp.

    Eventhough I am receiving 8bytes of data but if I try to print it is simply showing garbage values. Any help for a fix on it ?

    NOTE: No third party Libraries have to be used.

    EDIT1: I am really new to this serialization concept .. But without doing serialization cant I send a structure via sockets ?

    EDIT2: When I try to send a string or an integer variable using the sendto and recvfrom functions I am receiving the data properly at receiver end. Why not in the case of a structure? If I don't have to use serializing function then should I send each and every member of the structure individually? This really is not a suitable solution since if there are 'n' number of members then there are 'n' number of lines of code added just to send or receive data.

  • Douglas Leeder
    Douglas Leeder over 14 years
    And in this case, int can have different sizes on the different machines as well.
  • unwind
    unwind over 14 years
    @Douglas: Absolutely true, and added the list. Thanks!
  • codingfreak
    codingfreak over 14 years
    @unwind - Are serialize_int and deserialize_int are standard functions ??
  • unwind
    unwind over 14 years
    @codingfreak: No, you need to define them to do the kind of serialization you need.
  • codingfreak
    codingfreak over 14 years
    @unwind - I did not get you ... ?? Should I send only individual members rather than sending whole structure at once via socket ??
  • Andrey Vlasovskikh
    Andrey Vlasovskikh over 14 years
    @codingfreak If you want to be more practical, use some standard serialization format, e. g. JSON, ASN.1, XML. There are lots of serializaton/parsing libraries available for such formats.
  • unwind
    unwind over 14 years
    @codingfreak: You should serialize individual fields, then send the entire buffer containing the serialized data with a single write(). This way, you know exactly what is in each bit. If you write a whole struct at once, you don't know.
  • codingfreak
    codingfreak over 14 years
    @unwind - Should I write serialize functions for each and every datatype ??
  • unwind
    unwind over 14 years
    @codingfreak: Yes. :) It's either that, or use some library that does it for you. This will probably require you to describe your datastructures to that library, though.
  • unwind
    unwind over 14 years
    @qrdl: So does my function, as documented. It will always serialize to big-endian. You can of course use htonX()/ntohX() functions too, but this tried to illustrate a more general approach.
  • codingfreak
    codingfreak over 14 years
    @mandrill - Let use suppose it is a complex one ---- a generic solution would be the best answer if possible ... ??
  • qrdl
    qrdl over 14 years
    @codingfreak I didn't say to use it on structure. It is proper way to serialise short or long integers, that's it
  • the_mandrill
    the_mandrill over 14 years
    If the format is more complex then I defer to the superior solutions given elsewhere in this thread! Alternatively serialise to something more generic such as XML or a SOAP-like wrapper.
  • the_mandrill
    the_mandrill over 14 years
    Ultimately the aim is to serialise your data in a portable manner, so conversion to and from a string is portable, simple and readable. It may not be the most secure or efficient method, but it doesn't require any 3rd party libraries.
  • codingfreak
    codingfreak over 14 years
    How do I re-convert back the string into the data that cane be placed into a struct .... it would be more complex I feel so ??
  • Jeremy Friesner
    Jeremy Friesner over 14 years
    To re-convert the string back into data you'd have to parse the string, using things like atof(), atoi(), sscanf(), etc. (well maybe not sscanf(), it's dangerous). You're right, parsing the string can get complex for non-simple data. I'd recommend using a 3rd party serialization library instead.
  • codingfreak
    codingfreak over 14 years
    @qrdl - But my question is how to send a structure via sockets ... even without using htons()/htonl() functions I am able to send and receive data properly using sendto() and recvfrom() functions .....
  • codingfreak
    codingfreak over 14 years
    @unwind - why should I serialize each and every variable i.e. int , char ? By using UDP sockets I can send integers, character and receive them properly on receiver end .... I am just using sendto and recvfrom calls ... and I am not doing any serialization in this case. What are you doing by doing right shifts ... converting hostbyte order to network byte order or viceversa ?? ... your method might get complex when my structure has enums and other types ...
  • unwind
    unwind over 14 years
    @codingfreak: The bullet list first in my answer tells you why. If you write a whole struct using sendto(), you have no control over those things. Trying to receive the struct on a different machine, or just using code built by different compiler, will be difficult. You must treat each field separately, so you know what you send. Read the links I provided.
  • codingfreak
    codingfreak over 14 years
    @unwind - I feel you are not getting my point - I understand why I should serialize a structure ... But since you are saying that I have to send each member of the structure individually ... Then I dont understand why I should again serialize each member of a structure --- Cant I send them as if I send a single int or char or string ...
  • codingfreak
    codingfreak over 14 years
    @unwind - Ifpossible can you point me to a link where I can get the working simple code on passing a structure through sockets ... I am really so confused and not able to understand through conversation might be because of my communication gap ...
  • unwind
    unwind over 14 years
    @codingfreak: I added another function, that shows how to combine the serialization functions with sending the result. It's really simple, I'm not sure what is confusing you so much about this. :|
  • unwind
    unwind over 14 years
    @qrdl: My functions assume nothing about the host's endianness.
  • qrdl
    qrdl over 14 years
    @unwind My bad. I'm sorry, I keep forgetting that shift is endianness-aware. I'll change my post
  • xryl669
    xryl669 almost 9 years
    This answer is not good. You can do it yourself like this only if you prefer to suffer. It's much easier to pack a structure (so member alignement is correct), using bitfield whenever required and let the compiler do the work for you, it'll do it better than your attempt.
  • unwind
    unwind almost 9 years
    @xryl669 Then you're dependent on the exact compiler and platform, which is a very bad idea for networking. It typically needs to be interoperable and independent of those things.
  • xryl669
    xryl669 almost 9 years
    @unwind: This is exactly used for networking most of the time (just read the header of the sockaddr et. al structs you are using), and think about how hard it would be to have developper do bit masking and shifting for setting a port in a sockaddr_in's structure without bitfield. You should pack your structure (with the right pragma), and handle 2 cases: big and little endian. Compiler are not dumb, when packing is required, there is only a single solution if your declaration is well written. In the end, it's much easier for the code user, and you iron out bugs, instead of adding them.
  • xyf
    xyf over 2 years
    Mind clarifying as to why a struct can't be passed to the function and it's better to serialize the data into an array and pass that over instead?