Reputation: 37
I'm trying to build a reliable UDP file transfer from server to client using c sockets. I know how to use UDP socket to send a file.
However, now I need to concatenate my reliable header along with the data to sent it.
My designed header is defined as follows:
struct RudpHeader
{
int seqNo;
int ackNo;
int ackFlag;
int advWin;
int finFlag;
}
Here is the part the sends the file
int remBytes = (int) myFileSize;
char msg[1024];
while (remBytes > 0)
{
bytesRead = fread(msg, 1, sizeof(msg), file);
remBytes = remBytes - bytesRead;
// Here I want to append the header to msg
n = sendto(socketFD, msg, sizeof(msg), 0,(struct sockaddr *)&clientAdd, cAddLen);
if (n < 0)
printf("Failed to send to client. \n");
bzero(msg, sizeof(msg));
}
So my questions are:
1) What is the best to prepend the header to the message? msg is an array of strings and the header is struct.
2) at the receiver part what is the best way to separate the header from the content.
Upvotes: 1
Views: 357
Reputation: 2186
Take a look at this discussion. It provides a very elegant and portable way to send and receiver a datagram with a structured header and data.
Upvotes: 0
Reputation: 2706
well i'm no expert in C, but here is how I would do it:
#define HEADER_SIZE 13
struct RudpHeader
{
int seqNo;
int ackNo;
int advWin;
unsigned char ackFlag:1;
unsigned char finFlag:1;
};
int main()
{
struct RudpHeader header;
char* packet_data = calloc(1, HEADER_SIZE + 1024);
char* buffer = &packet_data[HEADER_SIZE]; //we can put data in here
header.seqNo = 100;
header.ackNo = 3542;
header.ackFlag = 1;
header.finFlag = 1;
//convert everything to network order before sending
header.seqNo = htonl(header.seqNo);
header.ackNo = htonl(header.ackNo);
header.advWin = htonl(header.advWin);
memcpy(&packet_data[0], &header.seqNo, 4);
memcpy(&packet_data[4], &header.ackNo, 4);
memcpy(&packet_data[8], &header.advWin, 4);
//no need to waste an int to send a bit, lets convert it
unsigned char flags = 0;
flags |= header.ackFlag;
flags |= header.finFlag << 1;
memcpy(&packet_data[12], &flags, 1);
//put some data in the buffer
char* msg = "Hello World!\0";
strcpy(buffer, msg);
//pretend you just received the packet and convert everything
//back to the hosts byte order
printf("Sequence Number: %d\nAcknowlegement Number: %d\nAdvWindow: %d\nFlags: %d\n",
ntohl(header.seqNo), ntohl(header.ackNo), ntohl(header.advWin), flags);
printf("Ack set: %d\nFin set %d\n", (flags&1), (flags&2));
printf("data: \"%s\"\n", buffer);
//now you can send the entire packet using variable 'packet_data'
//sendto(socket, packet_data, HEADER_SIZE + data_length, 0, sockaddr);
//either reuse the packet or destroy it(or just put it on the stack)
free(packet_data);
return 0;
}
note that I changed how the struct looks, a flag is not a integer so no need to waste data on the header. HEADER_SIZE == 4+4+4+1 which is 3 ints, 1 byte. you should never ever send a struct across the network, and never assume the two machines have the same byte order. htonl converts a 32 bit number to network byte order and ntohl converts it to host order. htons and ntohs is the same thing, it just converts 16 bits instead. also when you receive a packet the buffer size will be (length of packet) - header. I hope I commented enough to make it obvious what is going on here.
Upvotes: 1
Reputation: 62583
The proper way to do this is to incorporate your header into the message. Good practice is to put the header length and version as the very first piece of data, this way you will know if the client and server are on the same version of protocol.
Once you've read the message, you can extract header from it, compare versions and perform actions based on the header contents.
Upvotes: 0