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.
* NOTE: 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 null
* 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通讯的服务端代码有一定的启示作用,完全可以在我们需要的时候重用。