Solved

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

Posted on 1997-08-25
13
2,028 Views
Last Modified: 2008-03-17
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
0
Comment
Question by:cpetty
  • 5
  • 3
  • 3
  • +1
13 Comments
 
LVL 51

Expert Comment

by:ahoffmann
ID: 1205636
try
setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, *mreq)
0
 

Author Comment

by:cpetty
ID: 1205637
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.
0
 
LVL 51

Expert Comment

by:ahoffmann
ID: 1205638
Sorry for lazy reading.
remove $ from IP*, I'm not shure about $mreq or *mreq.
0
 

Author Comment

by:cpetty
ID: 1205639
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.
0
 
LVL 51

Expert Comment

by:ahoffmann
ID: 1205640
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.
0
 

Author Comment

by:cpetty
ID: 1205641
I appreciate your effort, but I that still does't answer my question.
Anyone else want to give it a try?
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 84

Expert Comment

by:ozo
ID: 1205642
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")

0
 
LVL 84

Expert Comment

by:ozo
ID: 1205643
(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

0
 
LVL 5

Accepted Solution

by:
julio011597 earned 100 total points
ID: 1205644
 Hello,

said that i've no experience with multicasting, neither know if your join statement may work, i may suggest this:

1. your setsockopt() must come _before_ the bind() - which has nothing to do with your error, but wouldn't let your code work anyway;

2. opening file "in.h" (on my system is /usr/include/netinet/in.h) i've found that:

IPPROTO_IP = 0x0, as you wrote, _but_

IP_ADD_MEMBERSHIP = 0x0C (12 decimal), while you wrote 0x13 (19 decimal) which corresponds to IP_PROXY, surely not what you meant.

Unless our systems differ on this, this could be your problem.

BTW, why didn't you define INADDR_ANY (=0x0), as well?

3. "in.h" defines ip_mreq to be (keep in mind: your OS may differ):

struct ip_mreq {
  struct in_addr imr_multiaddr;
  struct in_addr imr_interface;
};

and, in_addr is just an unsigned int;

you should check wether your join() can deal with struct memeber's alignment issues - this is bread for Perl gurus!


4. regarding the syntax of setsockopt(), your way seems the good one (no pointers in Perl!):

setsockopt(SOCKET, $IPPROTO_IP, $IP_ADD_MEMBERSHIP, $mreq);

HTH, julio

0
 
LVL 5

Expert Comment

by:julio011597
ID: 1205645
Ah, another thing:

keep in mind ozo's comment as well :)

-julio
0
 

Author Comment

by:cpetty
ID: 1205646
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!
0
 
LVL 84

Expert Comment

by:ozo
ID: 1205647
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;

0
 

Author Comment

by:cpetty
ID: 1205648
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?
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Many time we need to work with multiple files all together. If its windows system then we can use some GUI based editor to accomplish our task. But what if you are on putty or have only CLI(Command Line Interface) as an option to  edit your files. I…
Checking the Alert Log in AWS RDS Oracle can be a pain through their user interface.  I made a script to download the Alert Log, look for errors, and email me the trace files.  In this article I'll describe what I did and share my script.
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
This video discusses moving either the default database or any database to a new volume.

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now