Link to home
Start Free TrialLog in
Avatar of cpetty
cpetty

asked on

What do I pass to setsockopt() to get my program to join a muticast group?

I've got a program that sends messages to a multicast address, and I
want to create a "listener" program to read those messages.

I'm using multicast so I can put just one packet on the network, and
be able to receive it from one or more listening workstations.

I'm not quite sure what to pass to getsockopt().
In a c example I've got they pass a pointer to a struct ip_mreq.
Can it be done in Perl?

Here's what I've got so far.

----------------------------------
#!/usr/local/bin/perl
use strict;
require 5.002;
use Socket;
use Sys::Hostname;
$| = 1;

my ( $count, $hisiaddr, $hispaddr, $histime, $buffer,
        $host, $iaddr, $paddr, $port, $proto, $mreq );

my($IP_ADD_MEMBERSHIP) = 0x13;
my($IPPROTO_IP)        = 0x0;

#$iaddr = gethostbyname(hostname());
$iaddr = inet_aton(0.0.0.0);
$proto = getprotobyname('udp');
$paddr = sockaddr_in(5010, $iaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!";
bind(SOCKET, $paddr)  || die "bind: $!";

# join multicast group
$mreq = join '', inet_aton(225.100.1.18), INADDR_ANY;

#Program always dies here
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, $mreq)
        or die "error joining multicast group: $!";

for (;;) {
        $buffer = '';
        ($hispaddr = recv(SOCKET, $buffer, 1000, 0)) || die "recv:
$!";
        ($port, $hisiaddr) = sockaddr_in($hispaddr);
        $host = gethostbyaddr($hisiaddr, AF_INET);
        print "received from $host:  $buffer\n";
        sleep 1;
}
--------------------------------------

Craig Petty
cpetty@westin.com
Avatar of ahoffmann
ahoffmann
Flag of Germany image

try
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, *mreq)
Avatar of cpetty
cpetty

ASKER

Pass a typeglob you say?  That didn't work.  I got the same error message "Invalid argument" when I ran the program.
I'm not sure that my $mreq variable is right.  For that matter I'm not sure the $IPPROTO_IP & $IP_ADD_MEMBERSHIP variables are right either; I got them straight out of /usr/include/netinet/in.h.
Sorry for lazy reading.
remove $ from IP*, I'm not shure about $mreq or *mreq.
Avatar of cpetty

ASKER

Tried setsockopt(SOCKET, IPPROTO_IP, IP_ADD_MEMBERSHIP, *mreq)
Script won't compile now.  'IPPROTO_IP' and 'IP_ADD_MEMBERSHIP' become bare words without the '$'.  My 'use strict;' statement won't let me compile with barewords; I took away 'use strict', but still get the error message at run-time "Invalid argument to setsockopt()".  Those constants aren't provided the Socket module, so I defined them myself using the info in the c header files.  I don't know whether I defined them correctly or not.  Expert, what do you think?  I'd be glad to supply more info if you need it.
I'm not shure either about your constants: [gs]etsockopt need the  SOL_SOCKET  and  SO_*  options defined in socket.h.
So, I suggest, that you test these constants (just for syntax, not for your functionality; means that the message 'Invalid argument ..' should disaper).

I'm going confused about the syntax too, 'cause I checked the Camel book (1'st edition) and there are 3 different notations for those parameters: SO_TYPE  or  $SO_TYPE  or  &SO_TYPE
My opinion is that the first one (SO_TYPE) is the right one.
Avatar of cpetty

ASKER

I appreciate your effort, but I that still does't answer my question.
Anyone else want to give it a try?
Avatar of ozo
Well, one apparent problem with the code is that you have
      inet_aton(225.100.1.18)
where I would have thought you would have meant
      inet_aton("225.100.1.18")

(225.100.1.18) of course parsing as (225.100) . (1.18) eq '255.1' . '1.18' eq '225.11.18'

and (0.0.0.0) eq (0.0) . (0.0) eq '0' . '0' eq '00'
but inet_aton('00') eq inet_aton('0.0.0.0') so you got lucky there

ASKER CERTIFIED SOLUTION
Avatar of julio011597
julio011597

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ah, another thing:

keep in mind ozo's comment as well :)

-julio
Avatar of cpetty

ASKER

It works!  Many thanks to those who helped me.  It turned out to be a combo of problems.  First I wasn't using quotes around my ip addresses (thanks ozo).  Second I was defining IP_ADD_MEMBERSHIP incorrectly.  I was using 0x23 when I should have used 23.  (on another platform I have it's in hex).  Thanks julio for making me double check that.

I didn't define INADDR_ANY since it's provided by socket.h.

Here's my final working code for those who care.
-----------begin------------
#!/usr/local/bin/perl
use strict 'vars';
require 5.002;
use Socket;
use Sys::Hostname;
$| = 1;

my ( $count, $hisiaddr, $hispaddr, $histime, $buffer,
        $host, $iaddr, $paddr, $port, $proto, $mreq );

#my($IP_ADD_MEMBERSHIP) = 0x23;
my($IP_ADD_MEMBERSHIP) = 23;
my($IPPROTO_IP)        = 0x0;

#$iaddr = gethostbyname(hostname());
$iaddr = inet_aton("0.0.0.0");
$proto = getprotobyname('udp');
$paddr = sockaddr_in(5010, $iaddr); # 0 means let kernel pick
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto)   || die "socket: $!";
bind(SOCKET, $paddr)                          || die "bind: $!";

# join multicast group
$mreq = join '', inet_aton("225.100.1.18"), INADDR_ANY;
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, $mreq)
        or die "error joining multicast group: $!";

for (;;) {
        $buffer = '';
        ($hispaddr = recv(SOCKET, $buffer, 1000, 0))        || die "recv: $!";
        ($port, $hisiaddr) = sockaddr_in($hispaddr);
        $host = gethostbyaddr($hisiaddr, AF_INET);
        print "received from $host:  $buffer\n";
        sleep 1;
}
--------end-------------

Multicasting is pretty cool when you can get it to work!  You should check it out!
Looks like you didn't find it neccesary to do setsockopt before your bind?
join is fine, although
$mreq = inet_aton("225.100.1.18") . INADDR_ANY;
may be a simpler equivalent.
Just one more comment, since I now have to pay points just to come back here;
You might make your IP_ADD_MEMBERSHIP usage look more like the provided PF_INET etc
by declareing it as
sub IP_ADD_MEMBERSHIP {23}
(which has an additional advantage that it's harder to accidentally change it)
Or, with the latest Perl, (which may mean changing your require 5.002)
use constant IP_ADD_MEMBERSHIP => 23;

Avatar of cpetty

ASKER

I like your suggestion about making a constant declaration.  I've never know how to do that.  I can't find the "use constant" syntax anywhere in the Perl documentation.  Is it in the Camel book?  If not where can I find it?