Summary
-------
`DatagramSocket`'s `connect` and `disconnect` methods do not throw an `IOException` while `DatagramChannel`'s corresponding methods do. The potential failure behaviour of the `DatagramSocket`'s methods should be clarified.
Problem
-------
The behaviour of `DatagramSocket` `connect` and `disconnect` methods when an IO error occurs is not clear. For instance, the two-args `DatagramSocket::connect` method currently might throw an Error, which is undocumented behavior and not user-friendly.
Solution
--------
Update the specification of `DatagramSocket::connect` and `DatagramSocket::disconnect` to document that an implementation may throw `UncheckedIOException`. In addition, and similar to what was done for `DatagramChannel`, an @apiNote will be added to `DatagramSocket::disconnect` to recommend closing the socket if an IO exception occurs, as the underlying socket might have been left in an inconsistent and unspecified state. The two-args `DatagramSocket::connect` implementation used to throw an `Error` if a `SocketException` occurred (for instance, if the socket was not bound and the implicit bind failed). This is undocumented behavior, and this will now be changed to throw the documented `UncheckedIOException` instead. Similarly the `DatagramChannel` socket adapter used to throw an Error if connect (two-args) or disconnect failed, and will now be changed to throw `UncheckedIOException` instead, in conformance to the updated specification.
As it appears that the current omission of `throws IOException` from the declaration of these methods was likely an oversight - one that cannot be easily fixed now since it would be source incompatible - an UncheckedIOException is now being thrown.
Specification
-------------
DatagramSocket::connect
/**
* Connects the socket to a remote address for this socket. When a
* socket is connected to a remote address, packets may only be
* sent to or received from that address. By default a datagram
- * socket is not connected.
+ * socket is not connected. If the socket is already closed,
+ * then this method has no effect.
*
- * <p>If the remote destination to which the socket is connected does not
- * exist, or is otherwise unreachable, and if an ICMP destination unreachable
- * packet has been received for that address, then a subsequent call to
- * send or receive may throw a PortUnreachableException. Note, there is no
- * guarantee that the exception will be thrown.
+ * <p> If this socket is not bound then this method will first cause the
+ * socket to be bound to an address that is assigned automatically,
+ * as if invoking the {@link #bind bind} method with a parameter of
+ * {@code null}. If the remote destination to which the socket is connected
+ * does not exist, or is otherwise unreachable, and if an ICMP destination
+ * unreachable packet has been received for that address, then a subsequent
+ * call to send or receive may throw a PortUnreachableException. Note,
+ * there is no guarantee that the exception will be thrown.
*
* ...
*
* @param address the remote address for the socket
*
* @param port the remote port for the socket.
*
* @throws IllegalArgumentException
* if the address is null, or the port is out of range.
*
* @throws SecurityException
* if a security manager has been installed and it does
* not permit access to the given remote address
*
+ * @throws UncheckedIOException
+ * may be thrown if connect fails, for example, if the
+ * destination address is non-routable
*
* @see #disconnect
+ * @since 1.2
*/
public void connect(InetAddress address, int port) {
* Connects this socket to a remote socket address (IP address + port number).
*
* <p> If given an {@link InetSocketAddress InetSocketAddress}, this method
- * behaves as if invoking {@link #connect(InetAddress,int) connect(InetAddress,int)}
- * with the given socket addresses IP address and port number.
+ * <p> If given an {@link InetSocketAddress InetSocketAddress}, this method
+ * behaves as if invoking {@link #connect(InetAddress,int) connect(InetAddress,int)},
+ * except that {@code SocketException} that may be raised are not wrapped in
+ * {@code UncheckedIOException}.
...
public void connect(SocketAddress addr) throws SocketException {
DatagramSocket::disconnect
/**
* Disconnects the socket. If the socket is closed or not connected,
* then this method has no effect.
*
+ * @apiNote If this method throws an UncheckedIOException, the socket
+ * may be left in an unspecified state. It is strongly
+ * recommended that the socket be closed when disconnect
+ * fails.
+ *
+ * @throws UncheckedIOException
+ * may be thrown if it fails to dissolve the
+ * association and restore the socket to a consistent state
+ *
* @see #connect
+ * @since 1.2
*/
public void disconnect() {