注:本文适合对象需对java NIO API的使用及异步事件模型(Reactor模式)有一定程度的了解,主要讲述使用java原生NIO实现一个TCP服务的过程及细节设计。
前文讲述了NIO TCP服务绑定过程的实现机制,现在可以开始讲述服务监听启动后如何和处理接入和数据传输相关的细节设计。
在NIO的接入类中有一个Reactor线程,用于处理OP_ACCEPT事件通知,如下:
private class AcceptThread extends Thread {
public void run() {
while (selectable) {
try {
int selected = selector.select();
if (selected > 0) {
accept();
}
// bind addresses to listen
bind0();
// unbind canceled addresses
unbind0();
} catch (Exception e) {
LOG.error("Unexpected exception caught while accept", e);
}
}
// if selectable == false, shutdown the acceptor
try {
shutdown0();
} catch (Exception e) {
LOG.error("Unexpected exception caught while shutdown", e);
}
}
}
private void accept() {
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
AbstractSession session = (AbstractSession) acceptByProtocol(key);
Processor processor = pool.get(session);
session.setProcessor(processor);
processor.add(session);
}
}
protected Session acceptByProtocol(SelectionKey key) {
if (key == null || !key.isValid() || !key.isAcceptable()) {
return null;
}
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = null;
try {
sc = ssc.accept();
if(sc == null) {
return null;
}
sc.configureBlocking(false);
} catch (IOException e) {
LOG.warn(e.getMessage(), e);
if(sc != null) {
try {
sc.close();
} catch (IOException ex) {
LOG.warn(ex.getMessage(), ex);
}
}
}
Session session = new TcpSession(sc, config);
return session;
}
为每一个接入的客户端通过调用NIO原生accept()方法返回一个SocketChannel的抽象,并封装成一个session对象(session的概念来自mina框架)
注意:此时与客户连接的通道尚未注册对读/写事件感兴趣,因为它的注册与前文绑定过程一样需要异步进行。
因此将封装通道的session转交给一个processor对象(io读写处理器,该概念也是来自mina),processor内部维持了一个新建session的队列,在其内部reactor线程循环中进行注册处理。
有关processor处理读写事件的细节设计见下文。