Java Failed to open input stream for socket: Socket is closed (Solved)
I’ve recently been debugging an old project I inherited that features a [mostly] Java server and C++ client. I think it was mostly written 10–15 years ago (Java 6 at the latest), and probably not touched a whole lot since then, so while it’s all too easy to complain about any code, let alone old code, and code written by anyone else (and I will), I do also have respect for the fact that it’s moderately complex and has generally worked for a long time.
Although there’s plenty of low-hanging fruit for improvement in the project, the killer has been an issue seen when the system isn’t started up or shut down in its preferred sequence and speed. I never identified a perfect recipe for reproducing the issue, but basically: sometimes the system would get into a state where some clients would connect, but then the server seemed to essentially stop letting anyone new in. The workaround was to stop all clients (luckily
the clients in this case were actually other servers within our [customers’] control), stop the server, start the server, and then start up the clients one by one. This is annoying with four servers; it’s a major pain with 40. (For better or worse, nobody currently has more than that.)
I spent a little bit of time fixing warnings and suggestions from current versions of Eclipse, including implementing a number of Java try-with-resource wrappers for things that previously were left open in various conditions. One of these ‘try-with-resource’ suggestions was in the Socket sock = serverSocket.accept();
loop. Without giving it as much thought as I should have, I wrapped that in try(Socket sock = serverSocket.accept();) { ... }
; perfunctory testing looked okay, and I moved on.
Timeouts
The real problem that I identified with the killer issue
I described above was that the server did way too much heavy lifting inside the while(true) { sock = serverSocket.accept(); ... }
loop/thread. Sometimes it took the server multiple tens of seconds before it finished an iteration through the loop (a problem for another post, perhaps), meaning that all other attempts to connect were either being flat-out refused or hanging at a TCP SYN/ACK. (This was not helped by the client applying a 20-second timeout to the socket. This meant that the socket currently being worked on could time out before the server got around to sending data, and the next socket in the queue could [client-initiated] time out before the server even started to work on it.) Although increasing the client timeout value did ameliorate some symptoms, it still wasn’t the proper
way of accepting/servicing connections, so far as I understand it.
Threads!
It’d be nice to have a tunable thread pool (ExecutorService?) for servicing these requests, but I wanted to prove the concept before going off into the weeds on that. I made a very basic public class ConnectionSetupThread extends Thread {...}
to just wrap the things that the main while loop used to call directly, and set the while(true) { ...accept(); ... }
loop to new ConnectionSetupThread(sock...).start();
after the accept()
.
After implementing this change (and, admittedly a few others at the same time), connections started dying immediately. The thread was getting kicked off, but essentially immediately, when it attempted to socket.getInputStream();
, it was throwing Failed to open input stream for socket: Socket is closed
exceptions. I puzzled over this for a few minutes before realizing my folly: try-with-resource was closing the socket right when it went out of scope / at the end of the loop. Oops.
It’s happily chugging along now with something like this:
while (true) { try { sock = _server.accept(); new ConnectionSetupThread(sock).start(); } catch (IOException e) { logger.log(Level.WARNING, "IOException during socket accept/setup", e); if (null != sock && ! sock.isClosed()) try { sock.close(); } catch (IOException ee) { logger.log(Level.WARNING, "Second IOException during socket close", ee); } } }Tags: error, Java, solved