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 }