How to get the response from a UDP socket

Problem. I set up a UDP socket connection. I send a message to the
socket, and the destination device receives the message correctly and
act upon it, so far so good, but the destination device would send a
message back to the host and I do not know how to capture that message
and print the message.

Here is what I do, the local port is $port1 and $IP1 and the destination
assume to be $port2 and $IP2

I do the following:

require 'socket'

$port1 = 5000

$IP1 = '10.15.0.21'

$port2 = 15000

$IP2 = '10.15.0.66'

sock = UDPSocket.open

sock.bind($IP1 , $port1)

sock.connect($IP2 , $port2)

server_thread = Thread.start do

  server = UDPSocket.open

  server.bind(nil, $port1)

  2.times { p server.recvfrom(64) }

end

# now I send the message to device.

sock.send (" my UDP message", 0)

# and immediately I enter this command

server_thread.join

But nothing happens. And no receiving message is being printed out,
while I am sure the device has sent some message back.

I just do not understand the receiving part of it.

Can any body help me with it.

Try running your server process without threads. You may find out that
sock.send is throwing an exception that you can't see because it quietly
kills your thread.

Don't use connections with UDP. They don't make sense in the first place.

···

On 6/19/06, Hamid Rasoulian <hrasoulian@netstreams.com> wrote:

Problem. I set up a UDP socket connection. I send a message to the
socket, and the destination device receives the message correctly and
act upon it, so far so good, but the destination device would send a
message back to the host and I do not know how to capture that message
and print the message.

Here is what I do, the local port is $port1 and $IP1 and the destination
assume to be $port2 and $IP2

I do the following:

require 'socket'

$port1 = 5000

$IP1 = '10.15.0.21'

$port2 = 15000

$IP2 = '10.15.0.66'

sock = UDPSocket.open

sock.bind($IP1 , $port1)

sock.connect($IP2 , $port2)

server_thread = Thread.start do

  server = UDPSocket.open

  server.bind(nil, $port1)

  2.times { p server.recvfrom(64) }

end

# now I send the message to device.

sock.send (" my UDP message", 0)

# and immediately I enter this command

server_thread.join

But nothing happens. And no receiving message is being printed out,
while I am sure the device has sent some message back.

I just do not understand the receiving part of it.

Can any body help me with it.

Hamid Rasoulian wrote:

sock.bind($IP1 , $port1)

...

  server.bind(nil, $port1)

Two sockets can't bind to the same port. This raises the error:

Errno::EADDRINUSE: Address already in use - bind(2)

You aren't seeing the error because it's raised in a thread. There are
three ways around this:

1. set Thread.current.abort_on_exception=true inside the server thread

2. set Thread.abort_on_exception=true gloablly.

3. put a begin-rescue-end clause around the whole thread code (inside
the Thread.start block).

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Hamid Rasoulian wrote:

sock.bind($IP1 , $port1)

...

  server.bind(nil, $port1)

Two sockets can't bind to the same port. This raises the error:<<<

They're on two different machines, note the two different IP addresses.
(Unless he's multihomed.). There are other problems with this code anyway.

He doesn't show the line that sends the data from the client to the server,
and he also doesn't show the client code that receives the reply. As far as
I can tell, he's trying to send data from the client to a server port which
is different from the one the server is listening on. And yet, he says the
server receives the request. So something is missing in the code sample.

Francis Cianfrocca wrote:

Hamid Rasoulian wrote:

sock.bind($IP1 , $port1)

...

  server.bind(nil, $port1)

Two sockets can't bind to the same port. This raises the error:<<<

They're on two different machines, note the two different IP addresses.
(Unless he's multihomed.). There are other problems with this code anyway.

Look again at OPs code (below). Both sockets, "server" and "sock" are
being bound to $port1, and "server" is bound to any address, so this
code will raise EADDRINUSE. It happens that way on my linux box, anyway.

require 'socket'

$port1 = 5000
$IP1 = '10.15.0.21'

$port2 = 15000
$IP2 = '10.15.0.66'

sock = UDPSocket.open
sock.bind($IP1 , $port1)
sock.connect($IP2 , $port2)

server_thread = Thread.start do
  server = UDPSocket.open
  server.bind(nil, $port1)
  2.times { p server.recvfrom(64) }
end

# now I send the message to device.
sock.send (" my UDP message", 0)

# and immediately I enter this command
server_thread.join

He doesn't show the line that sends the data from the client to the server,
and he also doesn't show the client code that receives the reply. As far as
I can tell, he's trying to send data from the client to a server port which
is different from the one the server is listening on. And yet, he says the
server receives the request. So something is missing in the code sample.

I think all of the above code is running on the "client" side (despite
the "server" variable name), and this program is talking to some other
socket that may not even be controlled by ruby code. So this is the
complete client code, with all the needed send and recv calls.

It might work without the sock.bind call.

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Joel:
So you think that all of the OP's code is client-side code? And he's just
using the word "server_thread" to indicate that he's looking for a response
on that thread, not that it's on the server?

If that's true, then he can dispense with the thread, the bind and the
connect, provided he knows how the "device" is sending a response packet to
him. Many services that accept UDP requests will return responses to the
same address and port that the requests came from. (This is usually an
ephemeral port.) Some services (like NTP) send responses to a well-known
port, to which the client must bind. Assuming the former, this code should
work:

u = UDPSocket.new
u.send( "UDP Message", 0, server_ip_address, server_port)
response = u.recv(4096)

Francis Cianfrocca wrote:

Joel:
So you think that all of the OP's code is client-side code? And he's just
using the word "server_thread" to indicate that he's looking for a response
on that thread, not that it's on the server?

Just my guess...

If that's true, then he can dispense with the thread, the bind and the
connect, provided he knows how the "device" is sending a response packet to
him. Many services that accept UDP requests will return responses to the
same address and port that the requests came from. (This is usually an
ephemeral port.) Some services (like NTP) send responses to a well-known
port, to which the client must bind. Assuming the former, this code should
work:

u = UDPSocket.new
u.send( "UDP Message", 0, server_ip_address, server_port)
response = u.recv(4096)

That's a good point, but the OP seemed to have a fixed port in mind that
the remote socket would reply to (port 5000). Maybe the remote process
doesn't use the sender's address. I can only guess...

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

>>>
That's a good point, but the OP seemed to have a fixed port in mind that
the remote socket would reply to (port 5000). Maybe the remote process
doesn't use the sender's address. I can only guess...
<<<

Revised version that assumes the server will reply to port 5000:

u = UDPSocket.new
u.bind( client_ip_address, 5000 ) # This is the only change.
u.send( "UDP Message", 0, server_ip_address, server_port)
response = u.recv(4096)

Observe, you can't specify nil as the bind-address, as the OP
did.

What makes you say that? I'm using this code in something, and it works
perfectly:

socket=UDPSocket.new
socket.bind('',500)
socket.connect("172.16.35.131",500)
socket.send(payload,0)

You don't need to bind() to send() but I needed to fix the source port at
500 for stuff to work. Bind()ing to a specific address seems to actually
causes weird connect() errors sometimes if you have lots of interfaces. It's
unusual for the server to ignore the client source port when responding, but
certainly not unheard of.

Cheers,

ben

···

-----Original Message-----
From: Francis Cianfrocca [mailto:garbagecat10@gmail.com]
Sent: Wednesday, June 21, 2006 3:14 AM
To: ruby-talk ML
Subject: Re: How to get the response from a UDP socket.

>
> >>>
> That's a good point, but the OP seemed to have a fixed port
in mind that
> the remote socket would reply to (port 5000). Maybe the
remote process
> doesn't use the sender's address. I can only guess...
> <<<

Revised version that assumes the server will reply to port 5000:

u = UDPSocket.new
u.bind( client_ip_address, 5000 ) # This is the only change.
u.send( "UDP Message", 0, server_ip_address, server_port)
response = u.recv(4096)

Observe, you can't specify nil as the bind-address, as the OP
did.

What makes you say that? I'm using this code in something, and it works
perfectly:

socket=UDPSocket.new
socket.bind('',500)
socket.connect("172.16.35.131",500)
socket.send(payload,0)

You don't need to bind() to send() but I needed to fix the source port at
500 for stuff to work. Bind()ing to a specific address seems to actually
causes weird connect() errors sometimes if you have lots of interfaces. It's
unusual for the server to ignore the client source port when responding, but
certainly not unheard of.
<<<

Your code passes an empty string to Ruby's Socket#bind, which works ok. The
OP's code passed nil which is NOT ok and gives an EINVAL exception when you
try to send.
The server only ignores the client source port when responding if it's
designed to respond on a well-known port. This isn't so unusual (NTP does
it). But that's the usual reason for binding on the client side, as your
example shows. (Sometimes you want to make sure you're sending from a
particular address, for firewall reasons for example.)
It's unusual to use connect with UDP. More typical is just to pass the
server address in the send call. I can't speak to your point about weird
errors on connect with specific addresses. If repeatable, that's a Ruby
problem, not a UDP problem.