Question

I'm making a C program that pass a structure via socket

This is my struct

typedef struct{
    char    type;           //message type
    char*   sender;         //sender
    char*   receiver;       //receiver
    unsigned int msglen;    //msg length
    char*   msg;            //text
} msg_t;

this is my send function:

void send_message(int socket, char* msg)
{
    msg_t message;

    bzero(&message,sizeof(message));
    message.msg = msg;

    if(send(socket,&message,sizeof(msg_t),0) < 0)
    {
        perror("ERROR: send fail\n");
    }
}

and this is my receive function:

msg_t rec_message(int socket)
{
    msg_t buff;

    bzero(&buff,sizeof(buff));
    if(recv(socket,&buff,sizeof(buff),0) < 0)
    {
        perror("ERROR: receive failed\n");
    }

    return buff;

}

When I send message like strings everything works fine, but when I switch to structure the client seems to send the message and then give me this:

ERROR: receive failed: connection reset by peer

and the server this:

ERROR: receive failed: invalid argument

What am I doing wrong?

Was it helpful?

Solution

The question has several problems that need to be addressed. Perhaps it would be best to focus, first, on the msg_t structure itself. Here is a model of what it probably looks like; both in memory, as well as 'on the wire' as it is transmitted:

msg_t

According to the above, msg_t is 40 bytes long. This can be confirmed by printing out it's size:

     printf("sizeof(msg_t): %zd\n", sizeof(msg_t));

"So what's with all the empty white blocks?"

In order to make thing speedy at run-time, the compiler 'aligns' each field in the 'msg_t' structure at "natural/native" offsets of the CPU addressing architecture. On my 64-bit system, that means each structure field will be aligned on an eight-byte offset; even if it means leaving empty, unused space in the structure. Notice that the offsets of the structure fields are: 0, 8, 16, 24, 32; all multiples of 8 bytes.

On a 32-bit system, you might find that these offsets are at multiples of 4 bytes.

While an 8-byte alignment of structure fields is optimum for memory access, it is not so great when structures are sent over the wire. It is preferable for wire/protocol structures to be aligned at 1-byte; thus eliminating unused 'filler' bytes in the structure.

One way to change the alignment of a structure (supported my many compilers, but perhaps not defined by the C language itself) is '#pragma pack()'; which is used as shown below:

#pragma pack(1)
typedef struct{
   char    type;           //message type
   char*   sender;         //sender
   char*   receiver;       //receiver
   unsigned int msglen;    //msg length
   char*   msg;            //text
} msg_t;
#pragma pack()

In the above structure definition, the first '#pragma pack(1)' causes the following structure to be 1-byte aligned. The next '#pragma pack()' returns the compiler to its default 8-byte alignment default. This "packed" structure looks like this:

packed msg_t

Next, examine the fields in the structure. The 'sender' field is a 'char *'. A 'char *' is an address where the sender string can be found on the 'sending' machine (or endpoint). To be blunt, this 'address' is of no value at all to the 'receiver' machine (or endpoint); as the 'receiver' has no access to the memory of the 'sender'.

The same is true of the 'receiver' field; and the 'msg' field. All of these are addresses of strings on the 'sender' machine; which are of no value to the 'receiver' machine.

Most likely, the 'intent' is to send the actual 'sender', 'receiver' and 'msg' strings. To do that, a structure similar to the following might be used:

#pragma pack(1)
typedef struct{
   char   type;           //message type
   char   sender[15];     //sender
   char   receiver[15];   //receiver
   char   msg[30];       //text
} msg_t;
#pragma pack()

This structure looks like this:

functional msg_t

Now, the actual strings are in the structure; not just their address in memory. This will do what was actually intended.

Unfortunately, it does limit the length of each string; and it also contains a lot of unused/wasted space. Perhaps it would be nice to remove that limitation and allow more flexibility. It might be better to send these fields like this:

non-fixed-size packet

Notice that each 'variable-length' string is prefixed with one byte that indicates the length of the string that follows. (This is how strings are stored in the PASCAL language). This byte allows the following string to be from 0-255 bytes long. No wasted space on the wire.

Unfortunately, this 'wire format' cannot be produced directly using C structures.

Lets go now to the structure defined in the question; with some slight modification:

typedef struct{
   char    type;           //message type
   char*   sender;         //sender
   char*   receiver;       //receiver
   char*   msg;            //text
} msg_t;

Notice that I have returned the structure to it's natural/native 8-byte alignment by eliminating the '#pragma pack()' stuff. I have also removed the 'msgLength' field (it is not really needed).

Most likely, the sender, reciever, and msg fields of the structure will be initialized to point to strings (perhaps allocated with malloc(), etc.). What you do to send this structure over the wire, using the efficient layout above, is to send each field individually.

First, send the one byte 'type'. Then send the one byte length of the sender 'string' [ie: strlen(sender) + 1). Then send the 'sender' string, followed by the one byte length of the receiver string, followed by the 'receiver' string, followed by the one byte length of the 'msg' string, followed by the 'msg' string.

On the 'receiver' endpoint, you first read the one-byte 'type' (which would clue you in that there will be three 'length-preceeded' strings to follow). Reading the next byte would tell you the size of the following string (and allow you to malloc() memory to the 'sender' field of the msg_t structure at the receiver endpoint). Then read the 'sender' string into exactly the right sized, malloc()ed memory. Do the same to read the receiver string length, and the receiver string; and finally, with the msg length, and string.

If you find a PASCAL string (limited to 255 bytes) a bit tight, change the length-preceeded value from one byte, to multiple bytes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top