Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Socket Programming and passing data structures in stream

Status
Not open for further replies.

ezeke1

Programmer
Mar 20, 2003
41
0
0
US
Hello All,
I'm doing some socket programming and I need to pass data structures in a tcp/ip stream. I understand how to send character arrays in the buffer and parse it on the other end because I can pick apart the data one byte at a time. However I'm having some difficulty grasping the concept of sending and parsing multibyte data structures.

Does anyone have any resources on how I can go about doing this?

Thanks in advance.
 
Here's a snippet from the sender code:
Code:
struct mystruct {
  int a;
  char b[50];
  double c;
};

struct mystruct s1;
int socket,retcode;

/* ... open socket */

retcode=send(socket,(char *)&s1,sizeof(struct mystruct),0);
And here's a snippet from the receiver code:
Code:
struct mystruct {
  int a;
  char b[50];
  double c;
};

struct mystruct s1;
int socket;

/* ... open socket */

retcode=recv(socket,(char *)&s1,sizeof(struct mystruct),0);

The send and recv functions' second parameter takes a pointer to a char (a buffer, basically). By casting the structure to a char array, you can send data directly from your structure, and receive the data into an idential structure. If both sides are running the same OS and platform, this will work ok, however if they are not (for example, going between a Sun running Solaris and a PC running Solaris) then you might have byte ordering issues for ints, longs, floats, and doubles (essentially, any basic type other than char, but structs as a whole will be ok).

To be a bit cleaner, you might want to receive into a char array and then take a pointer to your struct and set it equal to the start of the char array.
 
Thank you for your attention Clairvoyant1332. If I typecast the structure into a character pointer when I send and as I receive do I need to typecast the received buffer back into the original structure type?

I tried it like you suggested and I receive the correct bytes, however if I print the buffer as a string, I get seg fault.

Code:
printf("buffer => %s\n", s1)
 
s1 is not a string,
need to print each element
CODE
printf("buffer => %d %s %f\n", s1.a,s1.b,s1.c)
/CODE

 
> do I need to typecast the received buffer back into the original structure type?
Yes. Type punning is tricky business, especially with structures and potential padding. As such, it's recommended that you typecast using pointers to unsigned char rather than char to avoid trap representations, and when the structure is in the punned form, be careful in how you access it because certain bytes will probably be invalid.
 
I'm able to send and receive structures now but my implementation is slightly different from the advice I've been given here. Whether this is correct or if it's a viable solution in the long run I do not know for sure.
My structure
Code:
typedef struct _mystruct {
    int size;
    float value1;
} mystruct
Sender code
Code:
....fill the structure with data....
....
mystruct msg;
j = write(socket, msg, sizeof(msg));
Receiver code
Code:
mystruct rcv_msg;
char buf[128];
int j;
j = read(socket, buf, sizeof(buf));
memcpy(&rcv_msg, buf, j);

By doing it this way I avoided typecasting the received buffer and memcpy solved all the hassle of parsing the buffer and filling in the data fields of my structure.

 
In you code here:
Code:
mystruct rcv_msg;
char buf[128];
int j;
j = read(socket, buf, sizeof(buf));
memcpy(&rcv_msg, buf, j);
... I don't see a direct relationship between buf and rcv_msg, as far as size goes. This could be a problem if sizeof(rcv_msg) isn't the same as sizeof(buf) (when you do the memcpy).

>>j = read(socket, buf, sizeof(buf));
The problem here is that you're never actually checking how much data you're getting from the read(). Just because you asked for sizeof(buf) bytes, doesn't mean you got it. The TCP/IP stack can give you chunks of data, depending on how it is receiving it from the network. It will of course, give you all the data in the correct order.

For example, if you send() a 4k block of data, the receiving end may have to issue a number of read() calls to get it all.

A safer way to do things, is to wrap the read() function inside your own function that will validate the amount of data it receives, prior to it being passed back to the core program. If you are dealing with fixed sized data structures, you might implement a wrapper function like so:

Code:
mystruct msg;

if (myread(socket, &msg, sizeof(msg)) != sizeof(msg))
{
  /* Error receiving, close socket + return */
}
/* Good read, all data present...*/

----------------------------

int myread(int socket, void *buffer, size_t bufsize)
{
   /* This function calls read as many times as necessary
      to fill the buffer with the specified number of bytes.
      It will only return when the buffer is full, or the
      socket goes bad.
    */
}
Other common ways to do this, are to:
- use a delimiter, read() bytes one at a time until you get that magic character (buffering IO when you get more advanced).
- use a small header of fixed size on each message that denotes the length of the full message. For example, send 4 bytes that have value 1234, telling the receiver to expect 1234 bytes of data to follow. Again, multiple read()s may be necessary.

A common trap can also be to read() too much data. If you do that, you could end up with 2 messages inside your read buffer (or even partial messages), and you will need to split and join accordingly.
 
Also, instead of this:
Code:
mystruct msg;
j = write(socket, msg, sizeof(msg));
Your sender code should look like this:
Code:
mystruct msg;
j = write(socket, &msg, sizeof(msg));
The write function is looking for a pointer to a memory location.
 
- use a small header of fixed size on each message that denotes the length of the full message. For example, send 4 bytes that have value 1234, telling the receiver to expect 1234 bytes of data to follow. Again, multiple read()s may be necessary.

Thank you for the insightful advice Hammer. I was actually working up towards doing what you said by having a small header inside the main data structure to be sent. This header would contain fields for size of proceeding message, type, and a counter

Code:
typedef _HEADER {
   int msg_size;
   int msg_type;
   int counter;
} HEADER

typedef struct _mystruct {
   HEADER head;
   int size;
   float value1;
} mystruct

I think I'll still need to do multiple reads however. The first read will just grab the header and I can use that to figure out how much data to continue reading.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top