views:

64

answers:

2

Hi guys,

I have a problem - I don't know the amount of data being sent to my UDP server.

The current code is this - testing in irb:

require 'sockets'
sock = UDPSocket.new
sock.bind('0.0.0.0',41588)

sock.read # Returns nothing
sock.recvfrom(1024) # Requires length of data to be read - I don't know this

I could set recvfrom to 65535 or some other large number but this seems like an unnecessary hack.

recvfrom and recvfrom_nonblock both throw away anything after that length specified.

Am I setting the socket up incorrectly?

+4  A: 

Note that UDP is a datagram protocol, not a stream like TCP. Each read from UDP socket dequeues one full datagram. You might pass these flags to recvfrom(2):

MSG_PEEK

    This  flag  causes  the  receive operation to return
    data from the beginning of the receive queue without
    removing that data from the queue. Thus, a subsequent
    receive call will return the same data.

MSG_WAITALL

    This flag requests that the operation block until the
    full request is satisfied. However, the call may still
    return less data than requested if a signal is caught,
    an error or disconnect occurs, or the next data to be
    received is of a different type than that returned.

MSG_TRUNC

    Return the real length of the packet, even when it was
    longer than the passed buffer. Only valid for packet sockets.

If you really don't know how large of a packet you might get (protocol limit is 65507 bytes, see here) and don't care about doubling the number of system calls, do the MSG_PEEK first, then read exact number of bytes from the socket.

Or you can set an approximate max buffer size, say 4096, then use MSG_TRUNC to check if you lost any data.

Also note that UDP datagrams are rarely larger then 1472 - ethernet data size of 1500 minus 20 bytes of IPv4 header minus 8 bytes of UDP header - nobody likes fragmentation.

Edit:

Socket::MSG_PEEK is there, for others you can use integer values:

MSG_TRUNC   0x20
MSG_WAITALL 0x100

Look into your system headers (/usr/include/bits/socket.h on Linux) to be sure.

Nikolai N Fetissov
Thanks for the response - but I don't understand how one would implement your suggestions in ruby. The constants you have described don't look to be available. I am constrained to what the library provides. Any examples would be greatly appreciated.
tgandrews
+2  A: 

Looking at the documentation for Ruby's recvfrom(), the argument is a maximum length. Just provide 65535 (max length of a UDP datagram); the returned data should be the sent datagram of whatever size it happens to be, and you should be able to determine the size of it the way you would for any stringlike thing in Ruby.

Russell Borogove