views:

262

answers:

5

How do I pass following message format from a UDP client to a UDP server?

-----------------------------
|Ver|p|fff| length| checksum|
----------------------------
| Customer id               |
-----------------------------
| Amount                    |
-----------------------------
|other                      |
-----------------------------

how do I write raw data from a structure onto the wire?

A: 

You will need to emulate parts of TCP, see all of section 5 on that FAQ. That said, once you've done that, look into using sendto, how you format the data is entirely up to you and how you intend to process it, but keep in mind that sockets are very good at sending byte-streams, and not much else.

Travis
why do you think the OP needs to emulate parts of TCP?
Coleman
+1  A: 

You have two choices.

1) You can marshall the structure into a wire format before sending and then unmarshall it after receiving. Unless you are sending a lot of data (or sending a little data very often), the overhead should not be noticeable.

2) Write the raw data onto the wire. If you do this, you'll want to make sure any numbers are in network order (see ntohl(), htonl(), ntohs(), htons()).

R Samuel Klatchko
how do I write raw data from a structure onto the wire?
AJ
+1  A: 

I'm assuming you have a structure for this. You will need to handle any big/little endian issues if you are transmitting to different architectures.

1) Either copy the structure to a buffer and pass the pointer to the buffer to sento

// rough example (not tested)
unsigned char buffer[1024];
struct yourstruct_t data;
memcpy_s(buffer, sizeof(buffer), &data, sizeof(yourstruct_t));
sendto(socket, buffer, sizeof(yourstruct_t), ...);

2) Pass the struct directly to sento

// rough example (not tested)
struct yourstruct_t data;
sendto(socket, (unsigned char *) &data, sizeof(yourstruct_t), ...);

Check out this Socket tutorial.

Ryan
+3  A: 

See related question/answers here: http://stackoverflow.com/questions/1229321/sending-structure-using-recvfrom-and-sendto

Keep in mind that if the client and server have different machine architectures (Big Endian vs Little Endian) or even different compilers/interpreters then sending a raw structure is not a good idea. I have seen cases where machines of the same architecture did not view a structure layout the same because the compilers used for the client and server code had optimized the struct storage differently.

So instead of sending the whole struct, consider encoding each field into a buffer using htons(), htonl() for integers, longs, etc. Then send that buffer rather than the original struct. On the server side decode the received buffer using ntohs(), ntohl() etc.. to reconstruct the struct.

Using UDP you will have to be aware that the network may lose the message. If your client and server on on the same local LAN the chances of a lost packet are low. If the client and server are talking across the Internet then the chances go up considerably. You can add acknowledgment messages and timeouts, but then you are starting down the path of re-inventing a reliable transport like TCP (e.g. you need to handle cases where the original message made it but only the acknowledgement was lost.) Not a terrible thing to do, but just be aware of what you are getting into.

You can instead use a TCP connection and possibly keeping it open to exchange more information between client and server. In this case you will probably want to add some delimiter values between messages and "escape" those delimiters when they occur within the message buffer payloads. The reason for this is that TCP really gives you a bidirectional byte stream, so you have to be careful about where one message ends and the next begins. UDP is "message oriented" such that one recvfrom will get you one complete message, whereas with TCP when you read bytes from the socket you could be reading the tail end of one message and the first few bytes of the next message, thus the need for delimiters.

Dave
A: 

As others have pointed out, though, you need to be very careful with endianess (especially with the length field) when sending across platforms. You also need to be careful with structure packing and alignment, I believe.

I would create the following structure and cast it as a char * in sendto/recvfrom.
You weren't specific about the field types, so I assumed unsigned char.

//assume the following structure on both the server and client
#pragma pack(1)    //windows only
typedef struct _data{
    unsigned char Ver;
    unsigned char p;
    unsigned char fff;
    unsigned short length;
    unsigned short checksum;
    unsigned long Customer_id;
    unsigned long Amount;
    unsigned char other[1485];  //1500 (normal MTU)-15 used
} data;

//on the server
data data_to_send;
data_to_send.length=sizeof(data);
data_to_send.Ver=1;
data_to_send.p=1;
//[...] fill in other data_to_send fields here
data_to_send.length += 64;  //just an example of using 64 bytes in "other"
data_to_send.checksum=calcChecksum(data_to_rx,data_to_rx.length);
sendto(socket, (char *) &data_to_send, data_to_send.length, ...);       

//on the client
data data_to_rx;
unsigned short checksum;
n=recvfrom(socket,(char *)&data_to_rx,sizeof(data),...);
//validate the incoming data_to_rx fields
if(data_to_rx.Ver!=1) //drop it
checksum=calcChecksum(data_to_rx,data_to_rx.length);
if(checksum!=data_to_rx.checksum) //drop it
[...]
Coleman