So, I am creating a simple multi-threaded game server (generic client/server) app in Java. I ran into a problem with threading that I thought I would post about. It may help someone else save a few hours of their time.
I have a class called TCPServer which implements Runnable (used for Java threading) and serves as a socket listener for my server. It waits for connections and then adds those connections to a connection manager. Each connection starts its own thread. The thing about threads is that they are easy to make, but melt my mind when it comes to managing them correctly. I thought I had a good handle on it. I did actually, but it made me think I was crazy for a few hours none-the-less.
So in my main class I have a command that starts the TCPServer. It creates a new one and then in the constructor, I start the thread. Well, as you may know, Thread.stop() is a deprecated and shunned method. Don't use it. I thought I was going to have to, but I forced myself to find the real reason for my issue.
Let me discuss the issue. The thread starts and a while loop checks a variable called receivingConnections. Theoretically, if this variable is set to false, it should break out of the thread and everything should be hunky dorey. I created a stop method which set this variable to false and then closed the ServerSocket.
Here's where the fun began. When I issued the command to execute the stop method, I kept getting a "socket closed" exception. This was being caught on the accept() method of ServerSocket. I couldn't figure out why it was throwing the exception. The socket was closed, true... but why did it keep trying to use the accept() method after the socket is closed.
I thought that it was due to the receivingConnections variable not being volatile. I changed this but still ran into the same issue.
The answer is rather crazy. You see, ServerSocket's accept() method just waits for a connection. The while loop that it was in was just stalled waiting on a new connection. Closing the socket triggered the exception. Even though right before the accept() method was called, I was checking if the socket was closed. My guess is that the accept() method simply starts its own infinite loop and waits on the new connection. As far as I can see, there's no way to break out of that.
The solution... set your flag variable to false. In my case it was the receivingConnections variable. Create a new client socket connection to the server and close it immediately. Then you can safely close the ServerSocket. This fools the accept() method, which forces an iteration of your while loop, which sees that your flag is now false, breaking out of the loop.