Hot questions for Using Transmission Control Protocol in tomcat

Question:

I use tomcat 8.5.4 to build a https based server on port 443 like this:

 94     <Connector
 95            protocol="org.apache.coyote.http11.Http11NioProtocol"
 96            port="8000" maxThreads="200"
 97            scheme="https" secure="true" SSLEnabled="true"
 98            keystoreFile="conf/rs_keystore.jks" keystorePass="ntsdawn"
 99            clientAuth="false" sslProtocol="TLS"/>

But a client use tcp to connect the port 8000, and then quickly close the tcp connection. After this, in my server the connection is CLOSE_WAIT status, and more important is, the CPU grows and the org.apache.tomcat.util.net.TLSClientHelloExtractor object was created very fast util the gc trigger:

root@nts32:/home/vcloud/apache-tomcat-8.5.4/logs# jmap -histo 14 | head

 num     #instances         #bytes  class name
----------------------------------------------
   1:       5159655      165108960  java.util.HashMap$Node
   2:          9138      131509512  [I
   3:       5148446      123562704  java.util.ArrayList
   4:       5145501      123492024  java.util.concurrent.LinkedBlockingQueue$Node
   5:       5145486      123491664  org.apache.tomcat.util.net.**TLSClientHelloExtractor**
   6:       3210008      102720256  java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
   7:       2162508       86500320  java.util.HashMap$KeyIterator

And the connection will stay at CLOSE_WAIT forever....

But, if I modify the server.xml use http but not https like this:

 70     <Connector port="8000" protocol="org.apache.coyote.http11.Http11NioProtocol"
 71                connectionTimeout="20000"
 72                redirectPort="8443" />

Then every thing is OK. The TCP connect come and my server will reply the FIN when client send the FIN.

Is anything wrong of my https configuration?


Answer:

There's a bug in tomcat 8.5.4 wherein TLS connections get into a loop when socket connection is dropped before TLS handshake is complete. Upgrading to tomcat 8.5.5 or above should fix this.

Below is the bug

https://bz.apache.org/bugzilla/show_bug.cgi?id=60035

Question:

I tried finding a question that covered my problem, but all I could find were lots of similar questions, but no answer that solved my problem.

I am creating a java webapp for Tomcat, that, between other things, must also act as a TCP server that handles multiple incoming connections: to do so, I run the TCP server code in a separate thread, which in turn uses a ExecutorService to create threads for each connection. The problem basically is that, when I stop Tomcat, the server thread never gets stopped (even if no one has connected yet) and hangs Tomcat, until I close the related process in the Task Manager.

So, this is the starting point of the program:

private TCPServer tcpServer;
Thread serverThread;

@Override
public void init() throws ServletException {            
    DBConn = getDBConn();
    if (DBConn != null) {
        //initiates loggers, reads configurations from a file, etc

        //starts TCP server
        tcpServer = new TCPServer(50001, 200);    
        serverThread = new Thread(tcpServer);
        serverThread.start();    

        //will be doing other stuff
    } else {
        //handles DB connection failure
    }
}

This is the TCPServer class:

public class TCPServer implements Runnable {

   private final int listeningPort;
   PrintWriter out;
   BufferedReader in;
   ServerSocket serverSocket;
   private final ExecutorService pool;

   public TCPServer(int port, int poolSize) {
       listeningPort = port;
       pool = Executors.newFixedThreadPool(poolSize);
   }    

   @Override
   public void run() {
       LinkedBlockingQueue<Socket> queue = new LinkedBlockingQueue<>();
       try {
           serverSocket = new ServerSocket(listeningPort);

           while (!Thread.currentThread().isInterrupted()) {
               pool.execute(new ConnectionHandler(queue));
               queue.put(serverSocket.accept());               
           }
       } catch (IOException | InterruptedException ex) {
           Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
       }
   }

   private class ConnectionHandler implements Runnable {
       private LinkedBlockingQueue<Socket> socketQueue;
       public ConnectionHandler(LinkedBlockingQueue<Socket> queue) {
           this.socketQueue = queue;
       }

       @Override
       public void run() {
           while (!Thread.interrupted() || !Thread.currentThread().isInterrupted()) {
               try (Socket clientSocket = socketQueue.take()) {
                   in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                   String line = in.readLine();
                   System.out.println("Incoming: " + line);

                   clientSocket.close();
               } catch (InterruptedException ex) {     
              Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE,    null, ex);
               } catch (IOException ex) {   
                Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
               }  
           }
       }


   public void close(){
       Thread.currentThread().interrupt();
       try {
           serverSocket.close();
       } catch (IOException ex) {  
                Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);           }
       pool.shutdownNow();
       }
}

I added, in the main class (the same where the init() above is), this override, that runs when Tomcat gets closed:

@Override
public void contextDestroyed(ServletContextEvent sce) {
    serverThread.interrupt();
    tcpServer.close();
    ServletContextListener.super.contextDestroyed(sce); 
}

I made so many edits trying to implement solutions that I found around on the internet, that surely some of this code is probably redundant or useless. Can someone give me pointers on what I should do to correctly stop the TCPServer thread?


Answer:

Thanks to this answer and Kayaman's comment, I revised the whole code and somehow got it working.

The init() override is the same, while the contextDestroyed override now is

@Override
public void contextDestroyed(ServletContextEvent sce) {        
    tcpServer.close();
    serverThread.interrupt();
    ServletContextListener.super.contextDestroyed(sce);
}

The TCPServer class now is:

public class TCPServer implements Runnable {
   private final int listeningPort;
   PrintWriter out;
   BufferedReader in;
   ServerSocket serverSocket;
   private final ExecutorService pool;

   public TCPServer(int port, int poolSize) {
       listeningPort = port;
       pool = Executors.newFixedThreadPool(poolSize);
   }    

   @Override
   public void run() {
       try {
           serverSocket = new ServerSocket(listeningPort);

           while (!Thread.currentThread().isInterrupted()) {    
               Socket clientSocket = serverSocket.accept();
               pool.submit(new ConnectionHandler(clientSocket)); 
           }

       } catch (IOException ex) {
           Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
       } finally {
           pool.shutdown();
       }
   }

   private class ConnectionHandler implements Runnable {
        private Socket sock;

       public ConnectionHandler(Socket sock){
           this.sock = sock;
       }

       @Override
       public void run() {
           while (!Thread.currentThread().isInterrupted()) {
               try{
                   in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
                   String line = in.readLine();
                   System.out.println("Incoming: " + line);

                   sock.close();

                  } catch (IOException ex) { 
                      Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
                  }   
              }
          }
      }

   public void close(){        
       try {
           serverSocket.close();
       } catch (IOException ex) {
           Logger.getLogger(TCPServer.class.getName()).log(Level.SEVERE, null, ex);
       }
   }
}