1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Jun 2004 : Initial release 
8         version:        Dec 2006 : South pacific version
9         
10         author:         Kris
11 
12 *******************************************************************************/
13 
14 module tango.net.device.Multicast;
15 
16 public  import  tango.net.InternetAddress;
17 public  import  tango.net.device.Datagram;
18 private import  tango.net.device.Berkeley;
19 
20 /******************************************************************************
21         
22         MulticastConduit sends and receives data on a multicast group, as
23         described by a class-D address. To send data, the recipient group
24         should be handed to the write() method. To receive, the socket is
25         bound to an available local adapter/port as a listener and must
26         join() the group before it becomes eligible for input from there. 
27 
28         While MulticastConduit is a flavour of datagram, it doesn't support
29         being connected to a specific endpoint.
30 
31         Sending and receiving via a multicast group:
32         ---
33         auto group = new InternetAddress ("225.0.0.10", 8080);
34 
35         // listen for datagrams on the group address (via port 8080)
36         auto multi = new MulticastConduit (group);
37 
38         // join and broadcast to the group
39         multi.join.write ("hello", group);
40 
41         // we are listening also ...
42         char[8] tmp;
43         auto bytes = multi.read (tmp);
44         ---
45 
46         Note that this example is expecting to receive its own broadcast;
47         thus it may be necessary to enable loopback operation (see below)
48         for successful receipt of the broadcast.
49 
50         Note that class D addresses range from 225.0.0.0 to 239.255.255.255
51 
52         see: http://www.kohala.com/start/mcast.api.txt
53                 
54 *******************************************************************************/
55 
56 class Multicast : Datagram
57 {
58         private InternetAddress group;
59 
60         enum {Host=0, Subnet=1, Site=32, Region=64, Continent=128, Unrestricted=255}
61 
62         /***********************************************************************
63         
64                 Create a writable multicast socket
65 
66         ***********************************************************************/
67 
68         this ()
69         {
70                 super ();
71         }
72 
73         /***********************************************************************
74         
75                 Create a read/write multicast socket
76 
77                 This flavour is necessary only for a multicast receiver
78                 (e.g. use this ctor in conjunction with SocketListener).
79 
80                 You should specify both a group address and a port to 
81                 listen upon. The resultant socket will be bound to the
82                 specified port (locally), and listening on the class-D
83                 address. Expect this to fail without a network adapter
84                 present, as bind() will not find anything to work with.
85 
86                 The reuse parameter dictates how to behave when the port
87                 is already in use. Default behaviour is to throw an IO
88                 exception, and the alternate is to force usage.
89                 
90                 To become eligible for incoming group datagrams, you must
91                 also invoke the join() method
92 
93         ***********************************************************************/
94 
95         this (InternetAddress group, bool reuse = false)
96         {
97                 super ();
98 
99                 this.group = group;
100                 /* Posix also seems to require to bind to the specific group, while
101                  * Windows does not allow binding to multicast groups. The most
102                  * portable way seems to always bind for posix, but not for other
103                  * systems.
104                  * Reference; http://markmail.org/thread/co53qzbsvqivqxgc
105                  */
106                 version (Posix) {
107                     native.addressReuse(reuse).bind(group);
108                 } else {
109                     native.addressReuse(reuse).bind(new InternetAddress(group.port));
110                 }
111         }
112         
113         /***********************************************************************
114                 
115                 Enable/disable the receipt of multicast packets sent
116                 from the same adapter. The default state is OS specific
117 
118         ***********************************************************************/
119 
120         Multicast loopback (bool yes = true)
121         {
122                 uint[1] onoff = yes;
123                 native.setOption (SocketOptionLevel.IP, SocketOption.MULTICAST_LOOP, onoff);
124                 return this;
125         }
126 
127         /***********************************************************************
128                 
129                 Set the number of hops (time to live) of this socket. 
130                 Convenient values are
131                 ---
132                 Host:           packets are restricted to the same host
133                 Subnet:         packets are restricted to the same subnet
134                 Site:           packets are restricted to the same site
135                 Region:         packets are restricted to the same region
136                 Continent:      packets are restricted to the same continent
137                 Unrestricted:   packets are unrestricted in scope
138                 ---
139 
140         ***********************************************************************/
141 
142         Multicast ttl (uint value=Subnet)
143         {
144                 uint[1] options = value;
145                 native.setOption (SocketOptionLevel.IP, SocketOption.MULTICAST_TTL, options);
146                 return this;
147         }
148 
149         /***********************************************************************
150 
151                 Add this socket to the listening group 
152 
153         ***********************************************************************/
154 
155         Multicast join ()
156         {
157                 native.joinGroup (group, true);
158                 return this;
159         }
160 
161         /***********************************************************************
162         
163                 Remove this socket from the listening group
164 
165         ***********************************************************************/
166 
167         Multicast leave ()
168         {
169                 native.joinGroup (group, false);
170                 return this;
171         }
172 }
173 
174 
175 /******************************************************************************
176 
177 *******************************************************************************/
178 
179 debug (Multicast)
180 {
181         import tango.io.Console;
182 
183         void main()
184         {
185                 auto group = new InternetAddress ("225.0.0.10", 8080);
186 
187                 // listen for datagrams on the group address
188                 auto multi = new Multicast (group);
189 
190                 // join and broadcast to the group
191                 multi.join.write ("hello", group);
192 
193                 // we are listening also ...
194                 char[8] tmp;
195                 auto bytes = multi.read (tmp);
196                 Cout (tmp[0..bytes]).newline;
197         }
198 }