javawindowsmacosudpdatagram

Why doesn't DatagramSocket#receive(DatagramPacket) block with macOS?


With following code,

import java.io.*;
import java.net.*;

class DatagramServer {

    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(null);
        socket.bind(new InetSocketAddress("127.0.0.1", 0));
        System.out.println(socket.getLocalSocketAddress());
        while (true) {
            // I just need client's address to send back some data!
            DatagramPacket packet = new DatagramPacket(new byte[0], 0);
            socket.receive(packet);
            System.out.println(packet.getSocketAddress());
        }
    }
}

In Windows it blocks as expected.

> java --version
java 11.0.8 2020-07-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.8+10-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.8+10-LTS, mixed mode)

> java DatagramServer

In macOS it doesn't and prints a bunch of received socket addresses.

$ java -version
openjdk version "11.0.14" 2022-01-18
OpenJDK Runtime Environment Temurin-11.0.14+9 (build 11.0.14+9)
OpenJDK 64-Bit Server VM Temurin-11.0.14+9 (build 11.0.14+9, mixed mode)

$ java DatagramServer 
/127.0.0.1:61206
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:0
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
/0.0.0.0:15922
...

Is this normal?


Solution

  • The reason we see these issues in MacOS, not on Windows is that the DatagramSocket and DatagramPacket class depends on the Operating System-specific implementation of JDK for socket connections. As the underlying implementation depends on OS, we see issues differently in Windows, macOS, or any Linux-based environment.

    In the context of the issue mentioned above, the DatagramSocket object is waiting for the DatagramPacket. The code creates a DatagramPacket with an empty byte array. When the byte array is empty the datagram packet is simply a request to the server for information. When the server has read all the data packets, the while loop terminates.

    To get a response from the server, the client creates a "receive" packet and uses the DatagramSocket receive method to receive the reply from the server. The receive method waits until a datagram packet destined for the client comes through the socket.

    Note that if the server's reply is somehow lost, the client will wait forever because of the no-guarantee policy of the datagram model. Normally, a client sets a timer so that it doesn't wait forever for a reply, if no reply arrives, the timer goes off and the client retransmits.

    So, here the client may be getting timed out and then the rebinding might fail, hence the socket is left in an unspecified state. This then further binds to the wild card address which is selected by the OS and it keeps repeating the loop indefinitely and not blocked.

    There is a known issue in MacOS. The fix for this issue in present in the later version of JDK (15+).
    Issue 1 : JDK-8235674 - Rebinding fail issue Oracle Bug

    Information in the link (JEP for JDK-15) :

    On macOS and Linux, invoking disconnect on the new implementation might require rebinding the underlying socket. This introduces the possibility that the rebinding might fail and leave the underlying socket in an unspecified state. Whereas the legacy implementation might have silently left the socket in an unspecified state.

    The Java version provided in the example is OpenJDK version 11.0.14 and 11.0.8 . Prior to JDK-15, If the rebinding failed then the socket is left in an unspecified state. DatagramSocket remains bound when disconnected, it is the close() method that would unbind it. The default behavior of the constructors is to bind the socket immediately.

    But here we have created an unbound DatagramSocket by DatagramSocket(null) as a constructor argument. When rebinding fails the socket moves to an unspecified state, there is a probability that the socket will remain allocated by the OS until the JVM process exits.

    Issue 2: JDK-8231259- DatagramChannel::disconnect re-binds socket to the wildcard address (macOS) and   Oracle Bug

    Above issue is fixed in JDK-14. A DatagramChannel is bound to a specific local address, connected, and then disconnect is invoked to dissolve the association. On macOS the disconnect rebinds the socket to the wildcard address. If the object is created using the no-arg constructor, you get a socket that is not bound, hence it returns the wildcard IP address 0.0.0.0. If the IP address is 0.0.0.0, the socket will be bound to the wildcard address, an IP address chosen by the kernel. 0.0.0.0 is a non-routable meta-address used to designate an invalid, unknown, or non-applicable target (a ‘no particular address’ place holder)

    Use links: JDK 17 : DatagramSocketDatagram Client ServerUDP loop
    StackOverflow : DataGramSocket questionsLocaladdress 0.0.0.0
    Fix : JDK-15 fixRelease notes