1 /* 2 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 4527345 7026376 6633549 26 * @summary Unit test for DatagramChannel's multicast support 27 * @build MulticastSendReceiveTests NetworkConfiguration 28 * @run main MulticastSendReceiveTests 29 * @run main/othervm -Djava.net.preferIPv4Stack=true MulticastSendReceiveTests 30 * @key randomness 31 */ 32 33 import java.nio.ByteBuffer; 34 import java.nio.channels.*; 35 import java.net.*; 36 import static java.net.StandardProtocolFamily.*; 37 import java.util.*; 38 import java.io.IOException; 39 40 public class MulticastSendReceiveTests { 41 42 static final Random rand = new Random(); 43 44 static final ProtocolFamily UNSPEC = new ProtocolFamily() { 45 public String name() { 46 return "UNSPEC"; 47 } 48 }; 49 50 /** 51 * Send datagram from given local address to given multicast 52 * group. 53 */ 54 static int sendDatagram(InetAddress local, 55 NetworkInterface nif, 56 InetAddress group, 57 int port) 58 throws IOException 59 { 60 ProtocolFamily family = (group instanceof Inet6Address) ? 61 StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; 62 DatagramChannel dc = DatagramChannel.open(family) 63 .bind(new InetSocketAddress(local, 0)) 64 .setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); 65 int id = rand.nextInt(); 66 byte[] msg = Integer.toString(id).getBytes("UTF-8"); 67 ByteBuffer buf = ByteBuffer.wrap(msg); 68 System.out.format("Send message from %s -> group %s (id=0x%x)\n", 69 local.getHostAddress(), group.getHostAddress(), id); 70 dc.send(buf, new InetSocketAddress(group, port)); 71 dc.close(); 72 return id; 73 } 74 75 /** 76 * Wait (with timeout) for datagram. 77 * 78 * @param expectedSender - expected sender address, or 79 * null if no datagram expected 80 * @param id - expected id of datagram 81 */ 82 static void receiveDatagram(DatagramChannel dc, 83 InetAddress expectedSender, 84 int id) 85 throws IOException 86 { 87 Selector sel = Selector.open(); 88 dc.configureBlocking(false); 89 dc.register(sel, SelectionKey.OP_READ); 90 ByteBuffer buf = ByteBuffer.allocateDirect(100); 91 92 try { 93 for (;;) { 94 System.out.println("Waiting to receive message"); 95 sel.select(5*1000); 96 SocketAddress sa = dc.receive(buf); 97 98 // no datagram received 99 if (sa == null) { 100 if (expectedSender != null) { 101 throw new RuntimeException("Expected message not received"); 102 } 103 System.out.println("No message received (correct)"); 104 return; 105 } 106 107 // datagram received 108 109 InetAddress sender = ((InetSocketAddress)sa).getAddress(); 110 buf.flip(); 111 byte[] bytes = new byte[buf.remaining()]; 112 buf.get(bytes); 113 String s = new String(bytes, "UTF-8"); 114 int receivedId = -1; 115 try { 116 receivedId = Integer.parseInt(s); 117 System.out.format("Received message from %s (id=0x%x)\n", 118 sender, receivedId); 119 } catch (NumberFormatException x) { 120 System.out.format("Received message from %s (msg=%s)\n", sender, s); 121 } 122 123 if (expectedSender == null) { 124 if (receivedId == id) 125 throw new RuntimeException("Message not expected"); 126 System.out.println("Message ignored (has wrong id)"); 127 } else { 128 if (sender.equals(expectedSender)) { 129 System.out.println("Message expected"); 130 return; 131 } 132 System.out.println("Message ignored (wrong sender)"); 133 } 134 135 sel.selectedKeys().clear(); 136 buf.rewind(); 137 } 138 } finally { 139 sel.close(); 140 } 141 } 142 143 144 /** 145 * Exercise multicast send/receive on given group/interface 146 */ 147 static void test(ProtocolFamily family, 148 NetworkInterface nif, 149 InetAddress group, 150 InetAddress source) 151 throws IOException 152 { 153 System.out.format("\nTest DatagramChannel to %s socket\n", family.name()); 154 try (DatagramChannel dc = (family == UNSPEC) ? 155 DatagramChannel.open() : DatagramChannel.open(family)) { 156 dc.setOption(StandardSocketOptions.SO_REUSEADDR, true) 157 .bind(new InetSocketAddress(0)); 158 159 // join group 160 System.out.format("join %s @ %s\n", group.getHostAddress(), 161 nif.getName()); 162 MembershipKey key; 163 try { 164 key = dc.join(group, nif); 165 } catch (IllegalArgumentException iae) { 166 if (family == UNSPEC) { 167 System.out.println("Not supported"); 168 return; 169 } 170 throw iae; 171 } 172 173 // send message to group 174 int port = ((InetSocketAddress)dc.getLocalAddress()).getPort(); 175 int id = sendDatagram(source, nif, group, port); 176 177 // receive message and check id matches 178 receiveDatagram(dc, source, id); 179 180 // exclude-mode filtering 181 182 try { 183 System.out.format("block %s\n", source.getHostAddress()); 184 185 // may throw UOE 186 key.block(source); 187 id = sendDatagram(source, nif, group, port); 188 receiveDatagram(dc, null, id); 189 190 // unblock source, send message, message should be received 191 System.out.format("unblock %s\n", source.getHostAddress()); 192 key.unblock(source); 193 id = sendDatagram(source, nif, group, port); 194 receiveDatagram(dc, source, id); 195 } catch (UnsupportedOperationException x) { 196 String os = System.getProperty("os.name"); 197 // Exclude-mode filtering supported on these platforms so UOE should never be thrown 198 if (os.equals("SunOS") || os.equals("Linux")) 199 throw x; 200 System.out.println("Exclude-mode filtering not supported!"); 201 } 202 203 key.drop(); 204 205 // include-mode filtering 206 207 InetAddress bogus = (group instanceof Inet6Address) ? 208 InetAddress.getByName("fe80::1234") : 209 InetAddress.getByName("1.2.3.4"); 210 System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), 211 nif.getName(), bogus.getHostAddress()); 212 try { 213 // may throw UOE 214 key = dc.join(group, nif, bogus); 215 216 id = sendDatagram(source, nif, group, port); 217 receiveDatagram(dc, null, id); 218 219 System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), 220 nif.getName(), source.getHostAddress()); 221 key = dc.join(group, nif, source); 222 223 id = sendDatagram(source, nif, group, port); 224 receiveDatagram(dc, source, id); 225 } catch (UnsupportedOperationException x) { 226 String os = System.getProperty("os.name"); 227 // Include-mode filtering supported on these platforms so UOE should never be thrown 228 if (os.equals("SunOS") || os.equals("Linux")) 229 throw x; 230 System.out.println("Include-mode filtering not supported!"); 231 } 232 } 233 } 234 235 public static void main(String[] args) throws IOException { 236 NetworkConfiguration config = NetworkConfiguration.probe(); 237 238 // multicast groups used for the test 239 InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); 240 InetAddress ip6Group = InetAddress.getByName("ff02::a"); 241 242 for (NetworkInterface nif: config.ip4Interfaces()) { 243 InetAddress source = config.ip4Addresses(nif).iterator().next(); 244 test(INET, nif, ip4Group, source); 245 test(UNSPEC, nif, ip4Group, source); 246 } 247 248 for (NetworkInterface nif: config.ip6Interfaces()) { 249 InetAddress source = config.ip6Addresses(nif).iterator().next(); 250 test(INET6, nif, ip6Group, source); 251 test(UNSPEC, nif, ip6Group, source); 252 } 253 } 254 }