From a5d41fd8cd6f416799a7527f34977a8eca9816e8 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Fri, 5 Jul 2024 11:49:07 +0100 Subject: [PATCH] 8335771 --- .../DatagramChannel/AdaptorMulticasting.java | 36 +++++-- .../DatagramChannel/AfterDisconnect.java | 98 +++++++++++++++---- .../nio/channels/DatagramChannel/Connect.java | 11 ++- .../ManySourcesAndTargets.java | 28 +++++- .../MulticastSendReceiveTests.java | 21 +++- .../channels/DatagramChannel/NotBound.java | 75 ++++++++++---- .../channels/DatagramChannel/Promiscuous.java | 42 ++++---- .../channels/DatagramChannel/ReceiveISA.java | 18 +++- .../DatagramChannel/SelectWhenRefused.java | 36 ++++++- .../DatagramChannel/SendReceiveMaxSize.java | 33 ++++++- .../nio/channels/DatagramChannel/Sender.java | 52 ++++++---- 11 files changed, 347 insertions(+), 103 deletions(-) diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptorMulticasting.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorMulticasting.java index 0c8da1ff9d9c9..38b3051effa08 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AdaptorMulticasting.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorMulticasting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,7 @@ import java.util.stream.Collectors; import static java.net.StandardSocketOptions.*; import static java.net.StandardProtocolFamily.*; +import static jdk.test.lib.NetworkConfiguration.isSameInterface; import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.net.IPSupport; @@ -295,8 +296,8 @@ static void testNetworkInterface(MulticastSocket s, // setNetworkInterface s.setNetworkInterface(ni); - assertTrue(s.getNetworkInterface().equals(ni)); - assertTrue(s.getOption(IP_MULTICAST_IF).equals(ni)); + assertTrue(isSameInterface(s.getNetworkInterface(), ni)); + assertTrue(isSameInterface(s.getOption(IP_MULTICAST_IF), ni)); InetAddress address = s.getInterface(); assertTrue(ni.inetAddresses().filter(address::equals).findAny().isPresent()); @@ -315,8 +316,8 @@ static void testNetworkInterface(MulticastSocket s, // setOption(IP_MULTICAST_IF) s.setOption(IP_MULTICAST_IF, ni); - assertTrue(s.getOption(IP_MULTICAST_IF).equals(ni)); - assertTrue(s.getNetworkInterface().equals(ni)); + assertTrue(isSameInterface(s.getOption(IP_MULTICAST_IF), ni)); + assertTrue(isSameInterface(s.getNetworkInterface(), ni)); // bad values for IP_MULTICAST_IF assertThrows(IllegalArgumentException.class, @@ -401,6 +402,10 @@ static void testLoopbackMode(MulticastSocket s) throws IOException { () -> s.setOption((SocketOption) IP_MULTICAST_LOOP, "badValue")); } + static int getPort(SocketAddress address) { + return ((InetSocketAddress) address).getPort(); + } + /** * Send a datagram to the given multicast group and check that it is received. */ @@ -412,7 +417,8 @@ static void testSendReceive(MulticastSocket s, InetAddress group) throws IOExcep assertTrue(s.getOption(IP_MULTICAST_IF) != null); SocketAddress target = new InetSocketAddress(group, s.getLocalPort()); - byte[] message = "hello".getBytes("UTF-8"); + String msg = "AdaptorMulticasting: " + System.nanoTime(); + byte[] message = msg.getBytes("UTF-8"); // send message to multicast group DatagramPacket p = new DatagramPacket(message, message.length); @@ -421,8 +427,22 @@ static void testSendReceive(MulticastSocket s, InetAddress group) throws IOExcep // receive message s.setSoTimeout(0); - p = new DatagramPacket(new byte[1024], 100); - s.receive(p); + do { + p = new DatagramPacket(new byte[1024], 100); + s.receive(p); + if (getPort(p.getSocketAddress()) == getPort(s.getLocalSocketAddress())) { + String str = new String(p.getData(), p.getOffset(), p.getLength(), "UTF-8"); + if (Arrays.equals(p.getData(), p.getOffset(), p.getLength(), message, 0, message.length)) { + System.out.format("Got expected message \"%s\" from %s%n", str, p.getSocketAddress()); + break; + } + System.out.println("Unexpected message received. Expected: " + msg); + System.out.println("Received message doesn't match - skipping: " + str); + } else { + System.out.println("Unexpected message received. Expected message from: " + s.getLocalAddress()); + System.out.println("Received message sender doesn't match - skipping: " + p.getSocketAddress()); + } + } while(true); assertTrue(p.getLength() == message.length); assertTrue(p.getPort() == s.getLocalPort()); diff --git a/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java b/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java index 6a80d686b305b..bd547bafd384c 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ */ import java.io.IOException; +import java.net.BindException; import java.net.InetAddress; import java.net.Inet6Address; import java.net.InetSocketAddress; @@ -46,6 +47,7 @@ import java.nio.channels.Selector; import java.util.HashMap; import java.util.Map; +import java.util.function.Predicate; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -54,6 +56,21 @@ public class AfterDisconnect { + public interface RetryableTest { + public void runTest() throws T; + } + + /** + * When calling {@link DatagramChannel#disconnect()} a {@link BindException} + * may occur. In which case we want to retry the test. + */ + public class BindExceptionOnDisconnect extends BindException { + BindExceptionOnDisconnect(BindException x) { + super(x.getMessage()); + initCause(x); + } + } + @Test public void execute() throws IOException { IPSupport.throwSkippedExceptionIfNonOperational(); @@ -61,30 +78,55 @@ public void execute() throws IOException { InetAddress lb = InetAddress.getLoopbackAddress(); // test with default protocol family - try (DatagramChannel dc = DatagramChannel.open()) { - System.out.println("Test with default"); - dc.bind(new InetSocketAddress(lb, 0)); - test(dc); - test(dc); - } - - // test with IPv6 socket - if (IPSupport.hasIPv6()) { - System.out.println("Test with IPv6 socket"); - try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET6)) { + System.out.println("Test with default"); + retry(() -> { + try (DatagramChannel dc = DatagramChannel.open()) { dc.bind(new InetSocketAddress(lb, 0)); test(dc); test(dc); } + }, BindExceptionOnDisconnect.class::isInstance, 5); + + // test with IPv6 socket + if (IPSupport.hasIPv6()) { + System.out.println("Test with IPv6 socket"); + retry(() -> { + try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET6)) { + dc.bind(new InetSocketAddress(lb, 0)); + test(dc); + test(dc); + } + }, BindExceptionOnDisconnect.class::isInstance, 5); } // test with IPv4 socket if (IPSupport.hasIPv4() && !preferIPv6) { System.out.println("Test with IPv4 socket"); - try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { - dc.bind(new InetSocketAddress(lb, 0)); - test(dc); - test(dc); + retry(() -> { + try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { + dc.bind(new InetSocketAddress(lb, 0)); + test(dc); + test(dc); + } + }, BindExceptionOnDisconnect.class::isInstance, 5); + } + } + + // retry the given lambda (RetryableTest) if an exception + // that satisfies the predicate (retryOn) is caught. + void retry(RetryableTest test, + Predicate retryOn, + int max) throws T { + for (int i=0; i < max; i++) { + try { + test.runTest(); + break; + } catch (Throwable t) { + if (i < max -1 && retryOn.test(t)) { + System.out.println("Got " + t + "; will retry"); + continue; + } + throw t; } } } @@ -111,7 +153,11 @@ void testLocalAddress(DatagramChannel dc) throws IOException { assertEquals(dc.getLocalAddress(), local); assertEquals(dc.getRemoteAddress(), remote); - dc.disconnect(); + try { + dc.disconnect(); + } catch (BindException x) { + throw new BindExceptionOnDisconnect(x); + } assertFalse(dc.isConnected()); assertEquals(dc.getLocalAddress(), local); assertTrue(dc.getRemoteAddress() == null); @@ -134,7 +180,11 @@ void testSocketOptions(DatagramChannel dc) throws IOException { Map, Object> map = options(dc); dc.connect(dc.getLocalAddress()); - dc.disconnect(); + try { + dc.disconnect(); + } catch (BindException x) { + throw new BindExceptionOnDisconnect(x); + } // check socket options have not changed assertEquals(map, options(dc)); @@ -168,7 +218,11 @@ void testSelectorRegistration(DatagramChannel dc) throws IOException { sel.selectNow(); dc.connect(dc.getLocalAddress()); - dc.disconnect(); + try { + dc.disconnect(); + } catch (BindException x) { + throw new BindExceptionOnDisconnect(x); + } // selection key should still be valid assertTrue(key.isValid()); @@ -210,7 +264,11 @@ void testMulticastGroups(DatagramChannel dc) throws IOException { MembershipKey key = dc.join(group, ni); dc.connect(dc.getLocalAddress()); - dc.disconnect(); + try { + dc.disconnect(); + } catch (BindException x) { + throw new BindExceptionOnDisconnect(x); + } // membership key should still be valid assertTrue(key.isValid()); diff --git a/test/jdk/java/nio/channels/DatagramChannel/Connect.java b/test/jdk/java/nio/channels/DatagramChannel/Connect.java index 082a3234cc5a7..01c83df08cae6 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/Connect.java +++ b/test/jdk/java/nio/channels/DatagramChannel/Connect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,8 @@ /* @test * @bug 4313882 7183800 + * @library /test/lib + * @build jdk.test.lib.Platform Connect * @run main/othervm Connect * @summary Test DatagramChannel's send and receive methods */ @@ -38,6 +40,8 @@ import java.util.concurrent.CompletionException; import java.util.stream.Stream; +import jdk.test.lib.Platform; + import static java.nio.charset.StandardCharsets.US_ASCII; public class Connect { @@ -114,9 +118,14 @@ public void run() { ByteBuffer bb = ByteBuffer.allocateDirect(MAX); bb.put(bytes); bb.flip(); + if (Platform.isOSX()) { + dc.bind(new InetSocketAddress(((InetSocketAddress)connectSocketAddress).getAddress(), 0)); + err.println("Initiator bound to: " + connectSocketAddress); + } err.println("Initiator connecting to: " + connectSocketAddress); dc.connect(connectSocketAddress); err.println("Initiator bound to: " + dc.getLocalAddress()); + assert !connectSocketAddress.equals(dc.getLocalAddress()); // Send a message err.println("Initiator attempting to write to Responder at " + connectSocketAddress); diff --git a/test/jdk/java/nio/channels/DatagramChannel/ManySourcesAndTargets.java b/test/jdk/java/nio/channels/DatagramChannel/ManySourcesAndTargets.java index ed4e9c0c02e04..6de019e8272bd 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/ManySourcesAndTargets.java +++ b/test/jdk/java/nio/channels/DatagramChannel/ManySourcesAndTargets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,8 @@ /* @test * @bug 8234805 8235193 * @summary Test DatagramChannel send/receive and that receive returns the expected - * sender address + * sender address. This test may fail intermittently on macOS if other datagram + * channel tests are running concurrently on the same host. * @run main/othervm ManySourcesAndTargets * @run main/othervm -Djava.net.preferIPv4Stack=true ManySourcesAndTargets */ @@ -63,6 +64,7 @@ public static void main(String[] args) throws Exception { try (DatagramChannel reader = DatagramChannel.open()) { // bind reader to wildcard address so it can receive from any address reader.bind(new InetSocketAddress(0)); + System.out.println("\nReader bound to: " + reader.getLocalAddress()); for (InetAddress address : addresses) { System.out.format("%n-- %s --%n", address.getHostAddress()); @@ -75,6 +77,7 @@ public static void main(String[] args) throws Exception { try (DatagramChannel sender = DatagramChannel.open()) { // bind sender to wildcard address so it can send to any address sender.bind(new InetSocketAddress(0)); + System.out.println("\nSender bound to: " + sender.getLocalAddress()); for (InetAddress address : addresses) { System.out.format("%n-- %s --%n", address.getHostAddress()); @@ -97,6 +100,11 @@ static void testSend(int count, InetAddress address, DatagramChannel reader) thr sender.bind(new InetSocketAddress(address, 0)); SocketAddress local = sender.getLocalAddress(); + System.out.println("Sender bound to: " + local); + if (((InetSocketAddress)local).getPort() == remotePort) { + System.out.println("testSend: Sender and reader have same port: skipping"); + return; + } byte[] bytes = serialize(local); SocketAddress previousSource = null; @@ -105,6 +113,8 @@ static void testSend(int count, InetAddress address, DatagramChannel reader) thr sender.send(ByteBuffer.wrap(bytes), remote); ByteBuffer bb = ByteBuffer.allocate(1000); + System.out.format("testSend: reader waiting to receive at: %s%n", + reader.getLocalAddress()); SocketAddress source = reader.receive(bb); System.out.format("received datagram from %s%n", source); @@ -138,11 +148,18 @@ static void testReceive(int count, DatagramChannel sender, InetAddress address) SocketAddress remote = reader.getLocalAddress(); + System.out.println("Reader bound to: " + remote); + if (((InetSocketAddress)local).getPort() == ((InetSocketAddress)remote).getPort()) { + System.out.println("testReceive: Sender and reader have same port: skipping"); + return; + } for (int i = 0; i < count; i++) { System.out.format("send %s -> %s%n", local, remote); sender.send(ByteBuffer.allocate(32), remote); ByteBuffer bb = ByteBuffer.allocate(1000); + System.out.format("testReceive: reader waiting to receive at: %s%n", + reader.getLocalAddress()); SocketAddress source = reader.receive(bb); System.out.format("received datagram from %s%n", source); } @@ -165,7 +182,12 @@ private static SocketAddress deserialize(byte[] bytes) throws Exception { private static Optional networkInterface(InetAddress ia) { try { - return Optional.ofNullable(NetworkInterface.getByInetAddress(ia)); + NetworkInterface nif = NetworkInterface.getByInetAddress(ia); + if (nif != null) { + System.out.format("Selecting interface %s[%d]%n\twith addresses:%n\t%s%n", + nif.getDisplayName(), nif.getIndex(), nif.inetAddresses().toList()); + } + return Optional.ofNullable(nif); } catch (SocketException e) { return Optional.empty(); } diff --git a/test/jdk/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java b/test/jdk/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java index c1ea04ba2160d..1c9eee748034d 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java +++ b/test/jdk/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,15 +99,25 @@ static void receiveDatagram(DatagramChannel dc, ByteBuffer buf = ByteBuffer.allocateDirect(100); try { + long elapsed = 0; for (;;) { System.out.println("Waiting to receive message"); + long start = System.nanoTime(); sel.select(5*1000); + long waited = (System.nanoTime() - start) / 1000_000; + elapsed += waited; + buf.clear(); SocketAddress sa = dc.receive(buf); // no datagram received if (sa == null) { if (expectedSender != null) { - throw new RuntimeException("Expected message not received"); + if (elapsed > 4800) { + throw new RuntimeException("Expected message not received"); + } else { + sel.selectedKeys().clear(); + continue; + } } System.out.println("No message received (correct)"); return; @@ -123,8 +133,8 @@ static void receiveDatagram(DatagramChannel dc, int receivedId = -1; try { receivedId = Integer.parseInt(s); - System.out.format("Received message from %s (id=0x%x)\n", - sender, receivedId); + System.out.format("Received message from %s (id=0x%x, length=%s)\n", + sender, receivedId, bytes.length); } catch (NumberFormatException x) { System.out.format("Received message from %s (msg=%s)\n", sender, s); } @@ -142,7 +152,6 @@ static void receiveDatagram(DatagramChannel dc, } sel.selectedKeys().clear(); - buf.rewind(); } } finally { sel.close(); @@ -160,6 +169,8 @@ static void test(ProtocolFamily family, throws IOException { System.out.format("\nTest DatagramChannel to %s socket\n", family.name()); + System.out.format("With interface=%s[%s]%n\twith bound addresses:%n\t%s%n", + nif.getDisplayName(), nif.getIndex(), nif.inetAddresses().toList()); try (DatagramChannel dc = (family == UNSPEC) ? DatagramChannel.open() : DatagramChannel.open(family)) { dc.setOption(StandardSocketOptions.SO_REUSEADDR, true) diff --git a/test/jdk/java/nio/channels/DatagramChannel/NotBound.java b/test/jdk/java/nio/channels/DatagramChannel/NotBound.java index 4cd9a03033642..bc4b7ef304fc9 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/NotBound.java +++ b/test/jdk/java/nio/channels/DatagramChannel/NotBound.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,19 +24,33 @@ /* @test * @bug 4512723 6621689 * @summary Test that connect/send/receive with unbound DatagramChannel causes - * the channel's socket to be bound to a local address + * the channel's socket to be bound to a local address. This test may fail + * intermittently on macOS if other datagram channel tests are running + * concurrently on the same host. + * @library /test/lib + * @build jdk.test.lib.Platform NotBound + * @run main/othervm NotBound */ import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import jdk.test.lib.Platform; public class NotBound { + static final CountDownLatch received = new CountDownLatch(1); + static void checkBound(DatagramChannel dc) throws IOException { if (dc.getLocalAddress() == null) throw new RuntimeException("Not bound??"); + System.out.println("Bound to: " + dc.getLocalAddress()); } // starts a thread to send a datagram to the given channel once the channel @@ -51,19 +65,38 @@ public void run() { Thread.sleep(50); local = (InetSocketAddress)dc.getLocalAddress(); } while (local == null); + System.out.format("receiver bound to: %s%n", local); - // send message to channel to wakeup receiver - DatagramChannel sender = DatagramChannel.open(); - try { - ByteBuffer bb = ByteBuffer.wrap("hello".getBytes()); - InetAddress lh = InetAddress.getLocalHost(); - SocketAddress target = - new InetSocketAddress(lh, local.getPort()); - sender.send(bb, target); - } finally { - sender.close(); + boolean isAnyLocal = local.getAddress().isAnyLocalAddress(); + int maxAttempts = 5; + int localPort = 0; + List llh = isAnyLocal + ? List.of(InetAddress.getLocalHost(), InetAddress.getLoopbackAddress()) + : List.of(local.getAddress()); + SocketAddress target = null; + for (int i = 0 ; i < maxAttempts ; i++) { + InetAddress lh = llh.get(i % llh.size()); + target = new InetSocketAddress(lh, local.getPort()); + // send message to channel to wakeup receiver + try (DatagramChannel sender = DatagramChannel.open()) { + ByteBuffer bb = ByteBuffer.wrap("NotBound: hello".getBytes()); + sender.send(bb, target); + System.out.format("Woke up receiver: sent datagram to %s from %s%n", + target, sender.getLocalAddress()); + localPort = ((InetSocketAddress)sender.getLocalAddress()).getPort(); + } + if (received.await(250, TimeUnit.MILLISECONDS)) break; + // if sender port and destination port were identical, which + // could happen on some systems, the receiver might not receive + // the datagram. So in that case we try again, bailing out if + // we had to retry too many times + if (localPort == local.getPort()) { + System.out.println("Local port and peer port are identical. Retrying..."); + } + } + if (localPort == local.getPort()) { + System.out.println("Couldn't find a port to send to " + target); } - } catch (Exception x) { x.printStackTrace(); } @@ -77,14 +110,12 @@ public static void main(String[] args) throws IOException { // connect dc = DatagramChannel.open(); try { - DatagramChannel peer = DatagramChannel.open() - .bind(new InetSocketAddress(0)); - int peerPort = ((InetSocketAddress)(peer.getLocalAddress())).getPort(); - try { + System.out.println("Checks that connect() binds the socket"); + try (DatagramChannel peer = DatagramChannel.open()) { + peer.bind(new InetSocketAddress(0)); + int peerPort = ((InetSocketAddress)(peer.getLocalAddress())).getPort(); dc.connect(new InetSocketAddress(InetAddress.getLocalHost(), peerPort)); checkBound(dc); - } finally { - peer.close(); } } finally { dc.close(); @@ -93,7 +124,8 @@ public static void main(String[] args) throws IOException { // send dc = DatagramChannel.open(); try { - ByteBuffer bb = ByteBuffer.wrap("ignore this".getBytes()); + System.out.println("Checks that send() binds the socket"); + ByteBuffer bb = ByteBuffer.wrap("NotBound: ignore this".getBytes()); SocketAddress target = new InetSocketAddress(InetAddress.getLocalHost(), 5000); dc.send(bb, target); @@ -105,9 +137,11 @@ public static void main(String[] args) throws IOException { // receive (blocking) dc = DatagramChannel.open(); try { + System.out.println("Checks that blocking receive() binds the socket"); ByteBuffer bb = ByteBuffer.allocateDirect(128); wakeupWhenBound(dc); SocketAddress sender = dc.receive(bb); + received.countDown(); if (sender == null) throw new RuntimeException("Sender should not be null"); checkBound(dc); @@ -118,6 +152,7 @@ public static void main(String[] args) throws IOException { // receive (non-blocking) dc = DatagramChannel.open(); try { + System.out.println("Checks that non-blocking receive() binds the socket"); dc.configureBlocking(false); ByteBuffer bb = ByteBuffer.allocateDirect(128); SocketAddress sender = dc.receive(bb); diff --git a/test/jdk/java/nio/channels/DatagramChannel/Promiscuous.java b/test/jdk/java/nio/channels/DatagramChannel/Promiscuous.java index 8db9c60c0b558..95e35ce89b402 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/Promiscuous.java +++ b/test/jdk/java/nio/channels/DatagramChannel/Promiscuous.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,9 +71,11 @@ static int sendDatagram(NetworkInterface nif, dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); byte[] msg = Integer.toString(id).getBytes("UTF-8"); ByteBuffer buf = ByteBuffer.wrap(msg); - System.out.format("Send message -> group %s (id=0x%x)\n", - group.getHostAddress(), id); + System.out.format("Send message -> group [%s]:%d (id=0x%x) nif:%s[%s]%n", + group.getHostAddress(), port, id, nif.getDisplayName(), nif.getIndex()); + System.out.format("bound address before send: %s%n", dc.getLocalAddress()); dc.send(buf, new InetSocketAddress(group, port)); + System.out.format("bound address after send: %s%n", dc.getLocalAddress()); } return id; } @@ -97,15 +99,25 @@ static void receiveDatagram(DatagramChannel dc, ByteBuffer buf = ByteBuffer.allocateDirect(100); try { + long elapsed = 0; for (;;) { System.out.println("Waiting to receive message"); + long start = System.nanoTime(); sel.select(5*1000); + long waited = (System.nanoTime() - start) / 1000_000; + elapsed += waited; + buf.clear(); SocketAddress sa = dc.receive(buf); // no datagram received if (sa == null) { if (datagramExpected) { - throw new RuntimeException("Expected message not received"); + if (elapsed > 4800) { + throw new RuntimeException("Expected message not received"); + } else { + sel.selectedKeys().clear(); + continue; + } } System.out.println("No message received (correct)"); return; @@ -121,8 +133,8 @@ static void receiveDatagram(DatagramChannel dc, int receivedId = -1; try { receivedId = Integer.parseInt(s); - System.out.format("Received message from %s (id=0x%x)\n", - sender, receivedId); + System.out.format("Received message from %s (id=0x%x, length=%s)\n", + sender, receivedId, bytes.length); } catch (NumberFormatException x) { System.out.format("Received message from %s (msg=%s)\n", sender, s); } @@ -140,7 +152,6 @@ static void receiveDatagram(DatagramChannel dc, } sel.selectedKeys().clear(); - buf.rewind(); } } finally { sel.close(); @@ -155,13 +166,14 @@ static void test(ProtocolFamily family, { System.out.format("%nTest family=%s%n", family.name()); + System.out.format("With interface=%s[%s]%n\twith bound addresses:%n\t%s%n", + nif.getDisplayName(), nif.getIndex(), nif.inetAddresses().toList()); - DatagramChannel dc1 = (family == UNSPEC) ? - DatagramChannel.open() : DatagramChannel.open(family); - DatagramChannel dc2 = (family == UNSPEC) ? - DatagramChannel.open() : DatagramChannel.open(family); + try (DatagramChannel dc1 = (family == UNSPEC) ? + DatagramChannel.open() : DatagramChannel.open(family); + DatagramChannel dc2 = (family == UNSPEC) ? + DatagramChannel.open() : DatagramChannel.open(family)) { - try { dc1.setOption(StandardSocketOptions.SO_REUSEADDR, true); dc2.setOption(StandardSocketOptions.SO_REUSEADDR, true); @@ -184,12 +196,8 @@ static void test(ProtocolFamily family, id = sendDatagram(nif, group2, port); - receiveDatagram(dc1, "dc1", false, id); receiveDatagram(dc2, "dc2", true, id); - - } finally { - dc1.close(); - dc2.close(); + receiveDatagram(dc1, "dc1", false, id); } } diff --git a/test/jdk/java/nio/channels/DatagramChannel/ReceiveISA.java b/test/jdk/java/nio/channels/DatagramChannel/ReceiveISA.java index 69df204e91557..ef764c85d711e 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/ReceiveISA.java +++ b/test/jdk/java/nio/channels/DatagramChannel/ReceiveISA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,10 +27,19 @@ * @summary Check that DatagramChannel.receive returns a new SocketAddress * when it receives a packet from the same source address but * different endpoint. + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * jdk.test.lib.Platform + * ReceiveISA + * @run main/othervm ReceiveISA + * */ import java.nio.*; import java.nio.channels.*; import java.net.*; + +import jdk.test.lib.Platform; + import static java.lang.System.out; public class ReceiveISA { @@ -44,10 +53,13 @@ public static void main(String args[]) throws Exception { DatagramChannel dc3 = DatagramChannel.open(); DatagramChannel dc4 = DatagramChannel.open()) { // client - dc3.socket().bind((SocketAddress) null); // bind server to any port + InetAddress lh = InetAddress.getLocalHost(); + InetSocketAddress dest = Platform.isOSX() + ? new InetSocketAddress(lh, 0) + : null; + dc3.socket().bind(dest); // bind server to any port // get server address - InetAddress lh = InetAddress.getLocalHost(); InetSocketAddress isa = new InetSocketAddress(lh, dc3.socket().getLocalPort()); ByteBuffer bb = ByteBuffer.allocateDirect(100); diff --git a/test/jdk/java/nio/channels/DatagramChannel/SelectWhenRefused.java b/test/jdk/java/nio/channels/DatagramChannel/SelectWhenRefused.java index d851e4f2b8148..fd15521f674e0 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SelectWhenRefused.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SelectWhenRefused.java @@ -39,8 +39,8 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; public class SelectWhenRefused { - static final int MAX_TRIES = 3; - static final String GREETINGS_MESSAGE = "Greetings from SelectWhenRefused!"; + static final int MAX_TRIES = 10; + static final String GREETINGS_MESSAGE = System.nanoTime() + ": Greetings from SelectWhenRefused!"; @Test public void test() throws IOException { @@ -49,9 +49,31 @@ public void test() throws IOException { // datagram sent to this address should be refused SocketAddress refuser = new InetSocketAddress(InetAddress.getLocalHost(), port); + System.out.println("Refuser is: " + refuser); - DatagramChannel dc = DatagramChannel.open().bind(new InetSocketAddress(0)); + DatagramChannel dc = null; + for (int i=0; i < MAX_TRIES; i++) { + dc = DatagramChannel.open(); + try { + dc.bind(new InetSocketAddress(0)); + } catch (Throwable t) { + dc.close(); + throw t; + } + if (((InetSocketAddress)dc.getLocalAddress()).getPort() == port) { + if (i < MAX_TRIES - 1) { + System.out.format("Refuser port has been reused by dc: %s, retrying...%n", + dc.getLocalAddress()); + continue; + } + System.out.format("Skipping test: refuser port has been reused by dc: %s%n", + dc.getLocalAddress()); + return; + } + break; + } dc1.close(); + assert dc != null; Selector sel = Selector.open(); try { @@ -119,7 +141,9 @@ static boolean testNoPUEBeforeConnection(DatagramChannel dc, // BindException will be thrown if another service is using // our expected refuser port, cannot run just exit. - DatagramChannel.open().bind(refuser).close(); + try (DatagramChannel dc2 = DatagramChannel.open()) { + dc2.bind(refuser); + } throw new RuntimeException("Unexpected wakeup"); } return true; // test passed @@ -166,7 +190,9 @@ static boolean testPUEOnConnect(DatagramChannel dc, // BindException will be thrown if another service is using // our expected refuser port, cannot run just exit. - DatagramChannel.open().bind(refuser).close(); + try (DatagramChannel dc2 = DatagramChannel.open()) { + dc2.bind(refuser); + } throw new RuntimeException("PortUnreachableException not raised"); } catch (PortUnreachableException pue) { System.out.println("Got expected PortUnreachableException " + pue); diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java index 31407ecb49359..8d74fd8a387a3 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.Platform; import jdk.test.lib.net.IPSupport; +import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -46,6 +47,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.util.ArrayList; @@ -139,7 +141,9 @@ public void testSendReceiveMaxSize(DatagramChannelSupplier supplier, int capacit var addr = new InetSocketAddress(host, port); try (var sender = supplier.open()) { - sender.bind(null); + sender.bind(new InetSocketAddress(host, 0)); + System.out.format("testSendReceiveMaxSize: sender: %s -> receiver: %s%n", + sender.getLocalAddress(), receiver.getLocalAddress()); if (!Platform.isOSX()) { if (sender.getOption(SO_SNDBUF) < capacity) sender.setOption(SO_SNDBUF, capacity); @@ -150,7 +154,18 @@ public void testSendReceiveMaxSize(DatagramChannelSupplier supplier, int capacit var sendBuf = ByteBuffer.wrap(testData); sender.send(sendBuf, addr); var receiveBuf = ByteBuffer.allocate(capacity); - receiver.receive(receiveBuf); + SocketAddress src; + int count = 0; + do { + receiveBuf.clear(); + src = receiver.receive(receiveBuf); + if (sender.getLocalAddress().equals(src)) break; + System.out.println("step1: received unexpected datagram from: " + src); + System.out.println("\texpected: " + sender.getLocalAddress()); + if (++count > 10) { + throw new AssertionError("too many unexpected messages"); + } + } while (true); sendBuf.flip(); receiveBuf.flip(); @@ -167,7 +182,17 @@ public void testSendReceiveMaxSize(DatagramChannelSupplier supplier, int capacit sendBuf = ByteBuffer.wrap(testData); sender.send(sendBuf, addr); receiveBuf = ByteBuffer.allocate(capacity - 1); - receiver.receive(receiveBuf); + count = 0; + do { + receiveBuf.clear(); + src = receiver.receive(receiveBuf); + if (sender.getLocalAddress().equals(src)) break; + System.out.println("step1: received unexpected datagram from: " + src); + System.out.println("\texpected: " + sender.getLocalAddress()); + if (++count > 10) { + throw new AssertionError("too many unexpected messages"); + } + } while (true); sendBuf.flip(); receiveBuf.flip(); diff --git a/test/jdk/java/nio/channels/DatagramChannel/Sender.java b/test/jdk/java/nio/channels/DatagramChannel/Sender.java index 8ab4268e3a47a..fcecdf9d79aa9 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/Sender.java +++ b/test/jdk/java/nio/channels/DatagramChannel/Sender.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,9 @@ /* @test * @bug 4669040 8130394 * @summary Test DatagramChannel subsequent receives with no datagram ready + * @library /test/lib + * @build jdk.test.lib.Platform Sender + * @run main Sender * @author Mike McCloskey */ @@ -36,6 +39,8 @@ import java.nio.ByteOrder; import java.nio.channels.DatagramChannel; +import jdk.test.lib.Platform; + public class Sender { static PrintStream log = System.err; @@ -46,25 +51,26 @@ public static void main(String[] args) throws Exception { } static void test() throws Exception { - Server server = new Server(); - Client client = new Client(server.port()); + try (Server server = new Server()) { + Client client = new Client(server.port()); - Thread serverThread = new Thread(server); - serverThread.start(); + Thread serverThread = new Thread(server); + serverThread.start(); - Thread clientThread = new Thread(client); - clientThread.start(); + Thread clientThread = new Thread(client); + clientThread.start(); - serverThread.join(); - clientThread.join(); + serverThread.join(); + clientThread.join(); - server.throwException(); - client.throwException(); + server.throwException(); + client.throwException(); + } } public static class Client implements Runnable { final int port; - Exception e = null; + volatile Exception e = null; Client(int port) { this.port = port; @@ -76,14 +82,17 @@ void throwException() throws Exception { } public void run() { - try { - DatagramChannel dc = DatagramChannel.open(); + try (DatagramChannel dc = DatagramChannel.open()) { ByteBuffer bb = ByteBuffer.allocateDirect(12); bb.order(ByteOrder.BIG_ENDIAN); bb.putInt(1).putLong(1); bb.flip(); InetAddress address = InetAddress.getLocalHost(); InetSocketAddress isa = new InetSocketAddress(address, port); + if (Platform.isOSX()) { + // avoid binding on wildcard on macOS + dc.bind(new InetSocketAddress(address, 0)); + } dc.connect(isa); clientISA = dc.getLocalAddress(); dc.write(bb); @@ -93,12 +102,16 @@ public void run() { } } - public static class Server implements Runnable { + public static class Server implements Runnable, AutoCloseable { final DatagramChannel dc; - Exception e = null; + volatile Exception e = null; Server() throws IOException { - dc = DatagramChannel.open().bind(new InetSocketAddress(0)); + // avoid binding to wildcard address on macOS + InetSocketAddress lo = Platform.isOSX() + ? new InetSocketAddress(InetAddress.getLocalHost(), 0) + : new InetSocketAddress(0); + dc = DatagramChannel.open().bind(lo); } int port() { @@ -149,6 +162,11 @@ public void run() { e = ex; } } + + @Override + public void close() throws IOException { + dc.close(); + } } }