0

I am currently using Ruby 1.8.7 to develop a Linux version of a Windows tool that checks for multicast data on up to 2 interfaces based on user configured multicast groups and ports.

I had a previous issue where I wasn't sure how to listen to multiple multicast channels but that has been resolved from a previous question.

This has led to the next problem. I need to listen to multiple multicast groups and:

  • Determine if any data was received on each of the specficic groups and report back which ones have data and which don't

  • Listen for data for a user configurable timeout period on all channels at the same time

I am trying to get this working in a separate piece of code where the multicast groups, ports and interfaces are all hard coded, once I get this sorted out and understood I will migrate it to my main program where user input and validation is already complete. I have updated my simple program code so now it looks like:

#!/usr/bin/ruby

require 'socket'
require 'ipaddr'
require 'timeout'

MCAST_GROUP_A
{
  :addr1 => '233.54.12.111',
  :addr2 => '233.86.230.111',
  :port => 26477,
  :bindaddr => '172.31.230.156'
}

MCAST_GROUP_B = 
{
  :addr => '233.54.12.111',
  :port => 18170,
  :bindaddr => '172.31.230.156'
}

ipA1 = IPAddr.new(MCAST_GROUP_A[:addr1]).hton + IPAddr.new(MCAST_GROUP_A[:bindaddr]).hton
ipA2 = IPAddr.new(MCAST_GROUP_A[:addr2]).hton + IPAddr.new(MCAST_GROUP_A[:bindaddr]).hton
ipB = IPAddr.new(MCAST_GROUP_B[:addr]).hton + IPAddr.new(MCAST_GROUP_B[:bindaddr]).hton

begin
  sockA = UDPSocket.open
  sockB = UDPSocket.open

  sockA.bind Socket::INADDR_ANY, MCAST_GROUP_A[:port]
  sockB.bind Socket::INADDR_ANY, MCAST_GROUP_B[:port]

  sockA.setsockopt Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ipA1
  sockA.setsockopt Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ipA2
  sockB.setsockopt Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ipB

  timeoutSeconds = 10
  Timeout.timeout(timeoutSeconds) do 
    msg1, info1 = sockA.recvfrom(1024)
    msg2, info2 = sockB.recvfrom(1024)
    #puts "MSG: #{msg} from #{info[2]} (#{info[3]})/#{info[1]} len #{msg.size}"
    puts "MSG: <garbled> from #{info1[2]} (#{info1[3]})/#{info1[1]} len #{msg1.size}"
    puts "MSG: <garbled> from #{info2[2]} (#{info2[3]})/#{info2[1]} len #{msg2.size}"
  end
  rescue Timeout::Error
    puts "Nothing received connection timedout\n"
ensure
  sockA.close
  sockB.close
end

But this has produced the problem in that it reports nothing is received because data on the B socket is not present.

I understand the reason for this is that the second recvfrom times out and we end up in the rescue part of the code because of that. But as this is my first Ruby program and I'm still learning about it I'm not sure what to do to resolve it.

In short I need to understand:

  • How to determine which channel data was received on for sockA as there are 2 multicast groups on that socket

  • How to monitor a configurable number of multicast groups for data at the same time and not have one failure to receive data cause an incorrect error to be output

Glen
  • 67
  • 1
  • 8

1 Answers1

0

To monitor multiple sockets at the same time, you use the select function. This question gives an example of how this is done in Ruby.

It is possible, at least in C, to get the destination address of an incoming packet using the IP_PKTINFO socket option and the recvfrom function, however it doesn't look like the Ruby APIs expose that functionality.

Community
  • 1
  • 1
dbush
  • 205,898
  • 23
  • 218
  • 273