The Java Socket API has been with us for over 20 years now. In that time it has been maintained and updated, but eventually, even the best-maintained code requires an overhaul to keep up to date with modern technology. With Java 13 the base classes that handle Socket interaction are being re-implemented to take advantage of the current state of Java and prepare for future improvements.
What is the Socket API
The Java socket API (java.net.ServerSocket and java.net.Socket) includes objects to allow direct control over the sockets that a server will listen to and sockets that will send data.
You can use a ServerSocket to listen to a port for a connection request and once accepted returns a Socket object that allows you to read data sent and write data back. The grunt work of these classes uses an internal implementation of SocketImpl, both of which use a SOCKS based implementation. This code is simple to understand and use but it has also been with us since Java 1.0 and it shows. Since its conception, this mix of legacy Java and C code has required increases to the default thread stack size and has reliability and concurrency issues appearing as time has gone on. We are now reaching a point where the only solutions is a complete overhaul.
JEP 353: Reimplement the Legacy Socket API
This JEP is to replace the implementation of SocketImpl with a new implementation called NioSocketImpl. This implementation shares the same infrastructure as the NIO (New I/O) implementation and integrates with existing buffer cache mechanisms so it does not need to use the thread stack. Along with these changes are several other advantageous changes such as java.lang.ref.Cleaner mechanism being used to close sockets should the SocketImpl implementation be garbage collected on Socket that has not been closed and timeout operations taking place with the Socket in non-blocking mode when being polled.
In order to minimise the risk of issues when re-implementing methods that have been in use for over 20 years, a system property has been introduced to use the original implementation.
Using -Djdk.net.usePlainSocketImpl=true will use the original implementation.
It should be noted that SocketImpl is a legacy SPI mechanism and was under-specified, the new implementation attempts to emulate unspecified behaviour where applicable. However, there are some corner-cases that may fail when using the new implementation, they can be viewed here. Using the above system property will rectify all but two of these.
- Input and Output streams returned from the previous implementation used to extend FileInputStream and FileOutputStream. The new implementation does not.
- ServerSockets using a custom or platform SocketImpl cannot accept connections that return Sockets with the other (custom or platform) type of SocketImpl.
What benefits does this provide?
The previous implementation was difficult to maintain and improve, with these changes the Java Socket API will be easier to maintain. Better maintenance will hopefully see the reliability of the socket code improving. The NIO implementation is also done at a lower level allowing the Socket and the ServerSocket class to remain unchanged.
These changes not only make the maintenance of this code easier but also future proofs the implementation. In the JDK there is currently a project called Project Loom. This project includes an interesting new approach to threads, introducing a concept of fibers (find out more in our previous Project Loom article). The NIO implementation can take advantage of Project Loom once it is released whereas the previous implementation would not be fit for purpose. This is possible as the NIO implementation uses java.util.concurrent locks instead of synchronized methods.