3

I've looked around StackOverflow and with Google and was unable to find a solution to my problem, so here's what I'm doing.

I have a Server for a Console application, and users connect to it with any Telnet Client. I'm handling Clients with three separate threads, one thread (ClientThread) controls all Program access to a Client, the ClientThread maintains two threads, one for processing input and one for sending output. The ClienThread reads the client socket and sends the input (if any) to the ProcessingThread which processes the input and performs the actions the user dictated. The output Thread manages a queue of output that needs to be sent and sends it each time it wakes up. When a user disconnects I occasionally use Output that's left in the Output "queue" before the connection is terminated so I wanted to set it up so that the connection was kept alive until the Output Queue is empty - however the Thread that reads the socket (and ultimately closes it when there is no more output) is hanging on the read and I'm unable to close it. This wasn't a problem before when, upon the user disconnecting, I just closed the Socket. I'll give you the relevant code.

public class ClientThread extends Thread {
   // Initialization and other code not pertaining to the problem 

   public void run() {
      while (connected) {
         try {
            String input = in.readLine();

            if (input != null) {
               processor.queue(TextUtility.processBackspaceChars(input));
               timeoutTimer.interrupt();
            }
         } catch (Exception e) {
            if (e.getMessage() != null && !(e.getMessage().equals("socket closed")))
               server.appendError("Issue reading from client: "
                     + getClientDisplayInfo() + "\n"
                     + "&r-The issue is:&w- " + e.getMessage() + "&r-.");
         }
      }

      while (disconnecting) {
         try {
            if (outputThread.hasOutput()) {
               sleep(10);

            } else {
               disconnecting = false;
               outputThread.disconnect();
               client.close();
            }
         } catch (Exception exc) { 
            server.appendError("Failed to close the Thread for client: " + getClientDisplayInfo());
         }
      }  
   }   

   public void disconnect() {
      try {
         connected = false;
         disconnecting = true;
         processor.disconnect();
         timeoutTimer.disconnect();
         in.close(); // This is the BufferedReader that reads the Socket Input Stream
         this.interrupt(); // This was my attempt to break out of the read, but it failed

      } catch (Exception e) {
         server.appendError("Failed to close the Thread for client: " 
               + getClientDisplayInfo());
      }
   }
}

I'm just curious how I can cancel the read without closing the Socket since I need to continue sending output.

Brandon Buck
  • 7,177
  • 2
  • 30
  • 51
  • While Stacker's response worked perfectly, as it's been brought up it would not scale with multiple clients. I doubt this program would ever support a large number of clients I am still attempting to make it as efficient as possible. The solution that I finally settled on was that when the OutputThread is sent the `disconnect` notice a flag is altered and as soon as it's queue of output empties it notifies the Client to close the socket. This keeps everything running the same way for those few seconds longer to clear the output queue. – Brandon Buck Sep 15 '11 at 04:16

2 Answers2

4

I guess it is similar to the problems I faced yesterday You could invoke is.available() on your input stream and check whether data is available before doing a (blocking) read.

Community
  • 1
  • 1
stacker
  • 68,052
  • 28
  • 140
  • 210
  • Thanks, this worked like a charm. After I posted this I realized I could have added a flag to the OutputThread that would tell the Client to close the Socket when it was empty. But your method works perfectly as well, thanks for the answer (in addition to Google/StackOverflow I suppose I should have referenced the API for Sockets). – Brandon Buck Sep 13 '11 at 21:52
  • This will involve continuous polling of the stream for new data. This will only work for a small number of clients. This will not scale at all. Normally you would never do this, but for small, low load programs this will work. – Jay Sep 13 '11 at 22:21
3

Don't call available(): either you waste CPU calling it too frequently, or you waste time not calling it frequently enough, so your reponse times suffer. Set a read timeout on the Socket and have the reader thread check a 'cancel' flag every time it trips; set the flag from whereever you want to set it from.

user207421
  • 305,947
  • 44
  • 307
  • 483