Click to See Complete Forum and Search --> : UDP sendto/receivefrom issue


portnov
September 15th, 2006, 11:51 AM
Hello, i came across small problem while trying to send unknown size string over Sock-UDP type.
In normal sock-tcp you need to send string size first and then the string itself, right? - works like a charm, however, when it comes to UDP there is small problem - no one can assure you the size will be received before the data or one of them can be loosed in the way.

Here is my 2 functions i made for sendto/recivefrom :

int Socket::SendTo (char *buf, char *host) {
int nBytes_sent;

struct sockaddr_in server_addr; // connector's address information
struct hostent *he = gethostbyname(host);

server_addr.sin_family = AF_INET; // host byte order
server_addr.sin_port = htons(HPORT); // short, network byte order
server_addr.sin_addr = *((struct in_addr *)he->h_addr);
memset(&(server_addr.sin_zero), '\0', 8); // zero the rest of the struct


// get our message size
long bufferLen = (long) strlen (buf) + 1;
// fix our byte ordering
bufferLen = htonl (bufferLen);

// send the message size
if ((nBytes_sent = sendto(fd, (char *) &bufferLen, sizeof (bufferLen), 0,
(struct sockaddr *)&server_addr, sizeof(struct sockaddr)))
== SOCKET_ERROR){
printf ("Send size Failed!\n");
return -1;
}

// re-fix our byte ordering
bufferLen = ntohl (bufferLen);

// send the actual message
if ((nBytes_sent = sendto(fd, (char *) buf, bufferLen, 0,
(struct sockaddr *)&server_addr, sizeof(struct sockaddr)))
== SOCKET_ERROR){
printf ("Send message Failed!\n");
return -1;
}

return nBytes_sent;
}



int Socket::ReceiveFrom (char *buf){

// the number of bytes we received
int nBytes;

struct sockaddr_in clientAdr; // connector's address information
int clientAdrLen = sizeof (clientAdr);

// the size of the message that is being sent
unsigned long messageSize;

// receive the message size first
if((nBytes = recvfrom(fd, (char *) &messageSize, sizeof(messageSize), 0,
(struct sockaddr *)&clientAdr, (socklen_t*)&clientAdrLen)) == SOCKET_ERROR)
{
printf ("Recv Failed...\n");
return -1;
}


if (nBytes == 0)
{
printf ("Client on %d disconnected\n", fd);
return -1;
}

// convert the message size to host ordering
messageSize = ntohl (messageSize);

// receive the reset of the message
if((nBytes = recvfrom(fd, buf, messageSize, 0,(struct sockaddr *)&clientAdr, (socklen_t*)&clientAdrLen))== SOCKET_ERROR)
{
printf ("Recv data Failed! : %d\n",nBytes);
return -1;
}

buf[messageSize] = '\0';
printf("got packet from %s\n",inet_ntoa(clientAdr.sin_addr));

return nBytes;
}


Using function above - one on server and one on client will resolve in getting in most cases only the size.

Can anyone show me the solution to UDP unknown size data send?
Thanks in advance.

MikeAThon
September 15th, 2006, 01:07 PM
In normal sock-tcp you need to send string size first and then the string itself, right? -
Pre-pending the string length is a common technique in TCP, but to understand the answer to your question, you need to understand why this technique is even needed.

The reason it's needed is that TCP is a stream-based protocol. As a consequence, the sender needs to tell the recipient how many bytes in the stream will correspond to the string, so the recipient will know where in the stream the string starts and where it ends.

UDP is not stream-based. It's message-based. The sender sends a message (a "datagram"), and the recipient receives it, intact, with nothing else before it and nothing else after it. (Or, since UDP is not a "reliable" protocol, the recipient might not receive anything at all -- see below for a comment on this aspect of UDP).

So, with UDP, it's common for a sender to simply sends the string (including its final NULL terminator, i.e., strlen(string)+1 bytes), and nothing else. The recipient receives it and uses it, directly.


Using function above - one on server and one on client will resolve in getting in most cases only the size.
Yes, according to the docs, that's exactly what's supposed to happen. The sender has sent a single datagram, and therefore the recipient will get exactly one datagram, and exactly one (and only one) chance to receive it. Your code used up that one chance when it called recvfrom the first time, with a 4-byte buffer. See http://msdn.microsoft.com/library/en-us/winsock/winsock/recvfrom_2.asp For message-oriented sockets, data is extracted from the first enqueued message, up to the size of the buffer specified. If the datagram or message is larger than the buffer specified, the buffer is filled with the first part of the datagram, and recvfrom generates the error WSAEMSGSIZE. For unreliable protocols (for example, UDP) the excess data is lost.
Your receipient code must be prepared to receive everything all at once, since it will lose the excess that it is not prepared to receive.

Can anyone show me the solution to UDP unknown size data send?
Send two messages (i.e., call sendto() twice). The first message tells the recipient the size, the second is the payload.

Note, however, that it isn't a good idea to send UDP datagrams that are much larger than MTU minus UDP header size (i.e, the datagrams should preferably be less than around 1500 - 20 = 1480 bytes). The reason is that UDP is an unreliable protocol, such that datagrams can be dropped for any reason at all, without any notification to the sender or to the recipient that a datagram was dropped. One common reason for dropping the datagram, is that the message was larger than MTU and therefore was fragmented, but that one router along the way did not receive all the fragments or otherwise could not reconstruct the full message. To minimize this as a source of data-loss, keep your UDP messages small.

Mike

portnov
September 15th, 2006, 01:23 PM
In other words, Mike, I should make predefined size limit for my sendto/recivefrom functions known by both server and client (say 1024) - and using it every call.
However, my problem is - what if i want to send buffer sized 24? - its 1000 garbage data sent (about 500%) ...
Seems to me there is no way making one universal function for udp send/receive - maybe I need to make few "size guided" (aka: sendToDword, sendToShortString....) .
Funny thing is , under *nix system this functions actually runs just the same way as tcp version, as long as the data arrives in correct order.

Thanks...

MikeAThon
September 15th, 2006, 01:47 PM
In other words, Mike, I should make predefined size limit for my sendto/recivefrom functions known by both server and client (say 1024) - and using it every call.
Yes. That's very typical.

However, my problem is - what if i want to send buffer sized 24? - its 1000 garbage data sent (about 500%) ...
You don't need to send the entire buffer; just send the data that's relevant. In other words, just because the max size of the buffer is 1024 doesn't mean that you must call sendto() for all 1024 bytes. Simply send the number of bytes that you need to. No garbage at all.

On the recipient's side, it knows that it must supply a buffer whose size is 1024 to recvfrom(), since it has pre-agreed with the sender that 1024 is the maximum size of any message. But recvfrom() will only fill the buffer up with what's actually received; it's not going to "pad" it at the end, or somehow wait for more to be sent from the sender (i.e., UDP is message-oriented). The return value from recvfrom will tell the recipient exactly how many bytes were copied into the buffer.

Mike

portnov
September 15th, 2006, 02:12 PM
Thank you Mike , helped me alot here, much appreciated...