UDPSocket - bidirectional communication through one socket?

Hi,
I’m trying to write threaded application which will communicate
(in both directions) through UDP (first thread for reading, second
for writing).
My problem is that I’m unable to get it to work … I admit I’m not expert
in this field, but it simply hangs on recvfrom(). The problem is that select()
reports ‘read data ready’ even if there are NO data and thus it remains
locked :frowning: I tried to wrap it in timeout(){} block … but the result
is still the same … it locks after first recvfrom(). :frowning:

Is there anybody who got this to work properly? Please, speak up … :slight_smile:

Thanks in advance,
W.

PS: Another weird thing is that I’m constantly getting
Invalid argument - "sendto(2)" (Errno::EINVAL) when sending data to
non-bound socket.

···


Wejn <lists+rubytalk(at)box.cz>
(svamberk.net’s Linux section, fi.muni.cz student, linuxfan)

    Bored?  Want hours of entertainment?         <<<
      Just set the initdefault to 6!             <<<

  I'm trying to write threaded application which will communicate
(in both directions) through UDP (first thread for reading, second
for writing).

Well, probably UDP is not the best ...

  My problem is that I'm unable to get it to work ... I admit I'm not expert
in this field, but it simply hangs on recvfrom(). The problem is that select()
reports 'read data ready' even if there are NO data and thus it remains
locked :frowning: I tried to wrap it in timeout(){} block ... but the result
is still the same ... it locks after first recvfrom(). :frowning:

How do you use ::select ?

Is there anybody who got this to work properly? Please, speak up ... :slight_smile:

pigeon% cat b.rb
#!/usr/bin/ruby
require 'socket'
$count = 4

m = Thread.new do
   a = UDPSocket.new
   a.bind('pigeon', 6666)
   mesg = 0
   while true
      if IO.select([a], nil, nil, 0)
         p a.recvfrom(1024)[0]
         mesg += 1
         break if mesg == $count
      else
         p "..."
         sleep 0.5
      end
   end
end

Thread.new do
   b = UDPSocket.new
   $count.times do |i|
      sleep 1
      b.send("hello #{i}", 0, 'pigeon', 6666)
   end
end.join

m.join
pigeon%

pigeon% b.rb
"..."
"..."
"hello 0"
"..."
"..."
"hello 1"
"..."
"..."
"hello 2"
"..."
"..."
"hello 3"
pigeon%

Guy Decoux

Well, probably UDP is not the best …

Well, for my project UDP is just one of the transports. BTW, TCP on fast
networks scales really bad. It’s congestion avoidance/control algorithms
aren’t good for the “slow ends, fast core” networks. My goal is to write
first (test only) implementation of the protocol stack for active routers

  • and it must have UDP as one of its transports (at the end it would have
    to be completely protocol independent, but that’s another story).

How do you use ::select ?

u = UDPSocket.new
u.bind(‘0.0.0.0’, ARGV[0])

part of thread1 (reader):

loop do
s = select([u], nil, nil, 0.01)
next if s.nil? or s[0].size.zero?
x = u.recvfrom(65535)

… processing here

end

… and here comes the rest - second thread2 (writer), …

Well … that’s great … but I need to use just one host:port pair for
each endpoint and that’s (imho) the problem. I’m unable to use ‘u’ from
the example above to simultaneously reading from/writing to network. :frowning:
If there’s no possibility to use it in the way:

 host1:X <---------------------> host2:X

i’ll have to use the (bad) model:

  host1:X ----------------------> host2:Y
  host1:Y <---------------------- host2:X

but I’m still trying to avoid that, if possible.

W.

···


Wejn <lists+rubytalk(at)box.cz>
(svamberk.net’s Linux section, fi.muni.cz student, linuxfan)

    Bored?  Want hours of entertainment?         <<<
      Just set the initdefault to 6!             <<<

If there's no possibility to use it in the way:

     host1:X <---------------------> host2:X

i'll have to use the (bad) model:
    
      host1:X ----------------------> host2:Y
      host1:Y <---------------------- host2:X

but I'm still trying to avoid that, if possible.

Well, I don't understand sorry

pigeon% cat b.rb
#!/usr/bin/ruby
require 'socket'
$count = 4

$sock = UDPSocket.new
m = Thread.new do
   $sock.bind('pigeon', 6666)
   mesg = 0
   while true
      if IO.select([$sock], nil, nil, 0)
         p $sock.recvfrom(1024)[0]
         mesg += 1
         break if mesg == $count
      else
         p "..."
         sleep 0.5
      end
   end
end

Thread.new do
   $count.times do |i|
      sleep 1
      $sock.send("pigeon ==> hello #{i}", 0, 'moulon', 6666)
   end
end.join

m.join
pigeon%

moulon% cat b.rb
#!/usr/bin/ruby
require 'socket'
$count = 4

$sock = UDPSocket.new
m = Thread.new do
   $sock.bind('moulon', 6666)
   mesg = 0
   while true
      if IO.select([$sock], nil, nil, 0)
         p $sock.recvfrom(1024)[0]
         mesg += 1
         break if mesg == $count
      else
         p "..."
         sleep 0.5
      end
   end
end

sleep 3 # stupid synchronisation
Thread.new do
   $count.times do |i|
      sleep 1
      $sock.send("moulon => hello #{i}", 0, 'pigeon', 6666)
   end
end.join

m.join
moulon%

moulon% b.rb
"..."
"..."
"..."
"..."
"..."
"..."
"..."
"pigeon ==> hello 0"
"..."
"..."
"pigeon ==> hello 1"
"..."
"..."
"pigeon ==> hello 2"
"..."
"..."
"pigeon ==> hello 3"
moulon%

pigeon% b.rb
"..."
"..."
"..."
"..."
"moulon => hello 0"
"..."
"..."
"moulon => hello 1"
"..."
"..."
"moulon => hello 2"
"..."
"..."
"moulon => hello 3"
pigeon%

Guy Decoux

Well, I don’t understand sorry

No, it seems you understand me pretty well :slight_smile:

pigeon% cat b.rb
[…snip…]
pigeon%

Woow! Thanks a lot! This code accomplishes exactly the job I need. :slight_smile:

Anyway, I’m still confused why my version doesn’t work :frowning:
I did it this way:

(1/wejn/ns) ~$ cat sock.rb
#!/usr/bin/env ruby
require ‘socket’

if ARGV.size != 3
puts “Usage: #{$0} ”
exit 0
end

BasicSocket.do_not_reverse_lookup = true

u = UDPSocket.new
u.bind(‘0.0.0.0’, ARGV[0])

t1 = Thread.new {
loop {
s = select([u], nil, nil, 0.01)
next if s.nil? or s[0].size.zero?
x = u.recvfrom(65535)
print "Received: "
p x
sleep 1
}
}

t2 = Thread.new {
loop {
u.send(ARGV[2], 0, ‘127.0.0.1’, ARGV[1]) rescue
puts “SendError: #{$!} – #{$!.type}”
puts “Sent: #{ARGV[2]}”
sleep 1
}
}

t2.join
t1.join
u.close

vim: set sw=4 ts=4 ai :

(1/wejn/ns) ~$

W.

···


Wejn <lists+rubytalk(at)box.cz>
(svamberk.net’s Linux section, fi.muni.cz student, linuxfan)

    Bored?  Want hours of entertainment?         <<<
      Just set the initdefault to 6!             <<<