JDK-8231880 : (dc) DatagramChannel::disconnect changes the port of the local address to 0 (lnx)
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.nio
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 14
  • Submitted: 2019-10-04
  • Updated: 2019-10-08
  • Resolved: 2019-10-08
Related Reports
CSR :  
Description
Summary
-------

DatagramChannel::disconnect may throw an exception, leaving the channel's socket in an unspecified state.

Problem
-------

On Linux systems, when a connected UDP DatagramChannel bound to an ephemeral port is disconnected, the local address port reverts to 0, and the
underlying native socket will be implicitly bound again to a new ephemeral port next time the socket is connected to an other remote party. This is a long standing behavior of the Linux kernel which is considered to be working as expected, but it clashes with the expectations (and specification) of the DatagramChannel and DatagramSocket APIs. DatagramSocket has a long standing workaround for the issue, which is to try and rebind the socket to its previous port when it detects that the local port switches to 0 after disconnect. DatagramChannel and DatagramSocket thus currently differ in behavior on Linux systems - where the behavior of DatagramChannel is very surprising for users of the Java API: 

 - leaving the underlying native socket 'unbound' after disconnect is called means that the channel is non functional until it gets connected again. 
 - it also means that the local port may silently change between two invocation of connect(). 
  
This current behavior is Linux specific, not specified by the Java API, and non intuitive, and can be very surprising for users of the API.

Solution
--------

The behavior of DatagramChannel::disconnect is changed to meet the user expectations, and to try and soften the differences across operating systems.
When disconnect() is called, if the port revolves to 0 after the association with the remote party is dissolved by the native code, DatagramChannel::disconnect will try and silently rebind the underlying socket to the previous value of the local port, so that the local address doesn't change after calling DatagramChannel::disconnect.

The Java API doesn't have to change as the new behaviour will match the current specification and the user expectation. However, there is a small time window where the previous ephemeral port will have been released by the system, and where it might have been reallocated to another party. In other words, there is an unlucky case where trying to rebind to the previous port might fail. In this case - an IOException will be thrown, even though the association with the remote party has already been dissolved.

The solution is to add an API note to the DatagramChannel::disconnect specification, stating that if DatagramChannel::disconnect throws an exception, the channel may be left in an unspecified state.

Specification
-------------

The following API note is added to DatagramChannel::disconnect

          * <p> If this channel's socket is not connected, or if the channel is
          * closed, then invoking this method has no effect.  </p>
          *
    +     * @apiNote If this method throws an IOException, the channel's socket
    +     * may be left in an unspecified state. It is strongly recommended that
    +     * the channel be closed when disconnect fails.
    +     *
          * @return  This datagram channel
          *
          * @throws  IOException
          *          If some other I/O error occurs


For convenience, the webrev can be seen here:
http://cr.openjdk.java.net/~dfuchs/webrev_8231260

Comments
I see there is already a release note planned for this change; moving to Approved.
08-10-2019