JIoEndpoint是tomcat中处理socket通讯服务器端的默认类。


主要功能有:

1:开启一个(默认)或者多个Deamon线程(Acceptor),接收客户端socket链接。

   关键代码:


public void init() throws Exception {
    ............
// Initialize thread count defaults for acceptor
        if (acceptorThreadCount == 0) {
            acceptorThreadCount = 1;
        }
    ..............
}
 public void start() throws Exception {
    .............
       // Start acceptor threads
            for (int i = 0; i < acceptorThreadCount; i++) {
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
                acceptorThread.setPriority(threadPriority);
                acceptorThread.setDaemon(daemon);
                acceptorThread.start();
            }
    ..............
}

       线程:Acceptor代码如下

/**
    * Server socket acceptor thread.
    */
   protected class Acceptor implements Runnable {
       /**
        * The background thread that listens for incoming TCP/IP connections and
        * hands them off to an appropriate processor.
        */
       public void run() {
           // Loop until we receive a shutdown command
           while (running) {
               // Loop if endpoint is paused
               while (paused) {
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       // Ignore
                   }
               }
               // Accept the next incoming connection from the server socket
               try {
                   Socket socket = serverSocketFactory.acceptSocket(serverSocket);
                   serverSocketFactory.initSocket(socket);
                   // Hand this socket off to an appropriate processor
                   if (!processSocket(socket)) {
                       // Close socket right away
                       try {
                           socket.close();
                       } catch (IOException e) {
                           // Ignore
                       }
                   }
               }catch ( IOException x ) {
                   if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
               } catch (Throwable t) {
                   log.error(sm.getString("endpoint.accept.fail"), t);
               }
               // The processor will recycle itself when it finishes
           }
       }
   }


2:采用线程池技术处理和客户端的socket通信

   tomcat默认的线程池(WorkStack)是一个数组模拟的栈,默认的大小是200.

   代码如下:


/**
     * Maximum amount of worker threads.
     */
    protected int maxThreads = 200;
    ................
    workers = new WorkerStack(maxThreads);
    .................


public class WorkerStack {
                                                                                                                                                                                                                                      
        protected Worker[] workers = null;
        protected int end = 0;
                                                                                                                                                                                                                                      
        public WorkerStack(int size) {
            workers = new Worker[size];
        }
                                                                                                                                                                                                                                      
        /**
         * Put the object into the queue. If the queue is full (for example if
         * the queue has been reduced in size) the object will be dropped.
         *
         * @param   object  the object to be appended to the queue (first
         *                  element).
         */
        public void push(Worker worker) {
            if (end < workers.length) {
                workers[end++] = worker;
            } else {
                curThreads--;
            }
        }
                                                                                                                                                                                                                                      
        /**
         * Get the first object out of the queue. Return null if the queue
         * is empty.
         */
        public Worker pop() {
            if (end > 0) {
                return workers[--end];
            }
            return null;
        }
                                                                                                                                                                                                                                      
        /**
         * Get the first object out of the queue, Return null if the queue
         * is empty.
         */
        public Worker peek() {
            return workers[end];
        }
                                                                                                                                                                                                                                      
        /**
         * Is the queue empty?
         */
        public boolean isEmpty() {
            return (end == 0);
        }
                                                                                                                                                                                                                                      
        /**
         * How many elements are there in this queue?
         */
        public int size() {
            return (end);
        }
                                                                                                                                                                                                                                      
        /**
         * Resize the queue. If there are too many objects in the queue for the
         * new size, drop the excess.
         *
         * @param newSize
         */
        public void resize(int newSize) {
            Worker[] newWorkers = new Worker[newSize];
            int len = workers.length;
            if (newSize < len) {
                len = newSize;
            }
            System.arraycopy(workers, 0, newWorkers, 0, len);
            workers = newWorkers;
        }
    }


   3:每当接收线程接收到客户端链接时,就从线程池中取出一个工作线程(Work)来处理socket通信。

关键代码如下:


/**
     * Process given socket.
     */
    protected boolean processSocket(Socket socket) {
        try {
            if (executor == null) {
                getWorkerThread().assign(socket);
            } else {
                executor.execute(new SocketProcessor(socket));
            }
        } catch (Throwable t) {
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }
/**
     * 当没有空闲的工作线程时,当前线程阻塞,直到线程池//workers.size()>0,
     */
    protected Worker getWorkerThread() {
        // Allocate a new worker thread
        synchronized (workers) {
            Worker workerThread;
            while ((workerThread = createWorkerThread()) == null) {
                try {
                    workers.wait();
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
            return workerThread;
        }
    }
 protected Worker createWorkerThread() {
        synchronized (workers) {
            if (workers.size() > 0) {
                curThreadsBusy++;
                return workers.pop();
            }
            if ((maxThreads > 0) && (curThreads < maxThreads)) {
                curThreadsBusy++;
                if (curThreadsBusy == maxThreads) {
                    log.info(sm.getString("endpoint.info.maxThreads",
                            Integer.toString(maxThreads), address,
                            Integer.toString(port)));
                }
                return (newWorkerThread());
            } else {
            //从此处代码可以看出,如何我们修改server.xml文件connector的maxThreads属性为负数,则线程数可能会一直增加。
                if (maxThreads < 0) {
                    curThreadsBusy++;
                    return (newWorkerThread());
                } else {
                    return (null);
                }
            }
        }
    }

  客户

/**
    * 创建并启动一个新的Daemon工作线程,等待处理客户端socket对象。
    */
   protected Worker newWorkerThread() {
       Worker workerThread = new Worker();
       workerThread.start();
       return (workerThread);
   }

4:Work线程的代码如下

protected class Worker implements Runnable {
        protected Thread thread = null;
        //available 来协调assign方法和await方法的调用次序。
        protected boolean available = false;
        protected Socket socket = null;
                                                                                                                                                    
        /**
         * Process an incoming TCP/IP connection on the specified socket.  Any
         * exception that occurs during processing must be logged and swallowed.
         * <b>NOTE</b>:  This method is called from our Connector's thread.  We
         * must assign it to our own thread so that multiple simultaneous
         * requests can be handled.
         *
         * @param socket TCP socket to process
         */
        synchronized void assign(Socket socket) {
            // Wait for the Processor to get the previous Socket
            while (available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
            // Store the newly available Socket and notify our thread
            this.socket = socket;
            available = true;
            notifyAll();
        }
                                                                                                                                                    
        /**
         * Await a newly assigned Socket from our Connector, or <code>null</code>
         * if we are supposed to shut down.
         */
        private synchronized Socket await() {
            // Wait for the Connector to provide a new Socket
            while (!available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
            // Notify the Connector that we have received this Socket
            Socket socket = this.socket;
            available = false;
            notifyAll();
            return (socket);
        }
        /**
         * The background thread that listens for incoming TCP/IP connections and
         * hands them off to an appropriate processor.
         */
        public void run() {
            // Process requests until we receive a shutdown signal
            while (running) {
                // Wait for the next socket to be assigned
                Socket socket = await();
                if (socket == null)
                    continue;
                // Process the request from this socket
                if (!setSocketOptions(socket) || !handler.process(socket)) {
                    // Close socket
                    try {
                        socket.close();
                    } catch (IOException e) {
                    }
                }
                // Finish up this request
                socket = null;
                recycleWorkerThread(this);
            }
        }
        /**
         * Start the background processing thread.
         */
        public void start() {
            thread = new Thread(this);
            thread.setName(getName() + "-" + (++curThreads));
            //是守护线程
            thread.setDaemon(true);
            thread.start();
        }
    }

    5:工作线程的回收

protected void recycleWorkerThread(Worker workerThread) {
      synchronized (workers) {
          workers.push(workerThread);
          curThreadsBusy--;
          workers.notify();
      }
  }

总结:从以上代码可以看出,默认线程池的最多能容纳200个工作线程,每个回收的线程都是阻塞的守护线程,当需要时可以直接拿来使用,不需要额外调用start()方法.仔细阅读该类的代码对我们实现socket通讯的服务端代码有一定的启示作用,完全可以在我们需要的时候重用。