1、回话过程创建
1) NioTcpServer.bind方法,打开ServerSocketChannel,并绑定地址SocketAddress。
public synchronized void bind(SocketAddress localAddress) {
Assert.assertNotNull(localAddress, "localAddress");
// check if the address is already bound
if (address != null) {
throw new IllegalStateException("address " + address + " already bound");
}
LOG.info("binding address {}", localAddress);
address = localAddress;
try {
//1、打开一个ServerSocketChannel并绑定注册地址
serverChannel = ServerSocketChannel.open();
serverChannel.socket().setReuseAddress(isReuseAddress());
serverChannel.socket().bind(address);
serverChannel.configureBlocking(false);
} catch (IOException e) {
throw new MinaRuntimeException("can't bind address" + address, e);
}
//2、向selectorLoop注册serverChannel
acceptSelectorLoop.register(true, false, false, false, this, serverChannel, null);
idleChecker = new IndexedIdleChecker();
idleChecker.start();
// it's the first address bound, let's fire the event
fireServiceActivated();
}
2)通过acceptSelectorLoop.register 方法向将channel的accept注册到selector中,具体如下
@Override
public void register(boolean accept, boolean connect, boolean read, boolean write, SelectorListener listener,
SelectableChannel channel, RegistrationCallback callback) {
if (IS_DEBUG) {
LOG.debug("registering : {} for accept : {}, connect: {}, read : {}, write : {}, channel : {}",
new Object[] { listener, accept, connect, read, write, channel });
}
int ops = 0;
if (accept) {
ops |= SelectionKey.OP_ACCEPT;
}
if (connect) {
ops |= SelectionKey.OP_CONNECT;
}
if (read) {
ops |= SelectionKey.OP_READ;
}
if (write) {
ops |= SelectionKey.OP_WRITE;
}
// TODO : if it's the same selector/worker, we don't need to do that we could directly enqueue
registrationQueue.add(new Registration(ops, channel, listener, callback));
// Now, wakeup the selector in order to let it update the selectionKey status
wakeup();
}
3)具体是向registrationQueue队列中添加注册。然后唤醒Selector,具体处理在NioSelectorLoop的SelectorWorker类的run方法处理。如下所示:
@Override
public void run() {
for (;;) {
try {
final int readyCount = selector.select();
if (readyCount > 0) {
final Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
final SelectionKey key = it.next();
final SelectorListener listener = (SelectorListener) key.attachment();
int ops = key.readyOps();
boolean isAcceptable = (ops & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isConnectable = (ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
boolean isReadable = (ops & SelectionKey.OP_READ) == SelectionKey.OP_READ;
boolean isWritable = (ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;
listener.ready(isAcceptable, isConnectable, isReadable, isReadable ? readBuffer : null,
isWritable);
// if you don't remove the event of the set, the selector will present you this event again
// and again
if (IS_DEBUG) {
LOG.debug("remove");
}
it.remove();
}
}
// new registration
while (!registrationQueue.isEmpty()) {
final Registration reg = registrationQueue.poll();
try {
SelectionKey selectionKey = reg.channel.register(selector, reg.ops, reg.listener);
if (reg.getCallback() != null) {
reg.getCallback().done(selectionKey);
}
} catch (final ClosedChannelException ex) {
// dead session..
LOG.error("socket is already dead", ex);
}
}
// tasks
while (!runnableQueue.isEmpty()) {
runnableQueue.poll().run();
}
} catch (final Exception e) {
LOG.error("Unexpected exception : ", e);
}
}
}
}
如上代码,最开始并没有客户端连接,selector.select执行后的结果为0,代码直接进入
while (!registrationQueue.isEmpty()) {
在这个方法的
reg.channel.register(selector, reg.ops, reg.listener);
处理逻辑中将ServerSocketChannel注册到Selector上面,监听的事件是accept,key的attachment为NioTcpServer。
4)客户端有连接进来,如果客户端有连接进入。则SelectorWorker类的run方法的
final int readyCount = selector.select();
的readyCount>0, SelectionKey的操作为accept, key的attchment即listener为NioTcpServer。NioTcpServer的ready方法将负责处理客户端的连接请求。
5)NioTcpServer的ready方法分析,具体代码如下:
@Override
public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer,
final boolean write) {
if (accept) {
LOG.debug("acceptable new client");
// accepted connection
LOG.debug("new client accepted");
createSession(getServerSocketChannel().accept());
如代码所示:ready的主要逻辑是接受客户端的请求,并创建Session操作。具体分析createSession的逻辑。
private synchronized void createSession(SocketChannel clientSocket) throws IOException {
SocketChannel socketChannel = clientSocket;
TcpSessionConfig config = getSessionConfig();
SelectorLoop readWriteSelectorLoop = readWriteSelectorPool.getSelectorLoop();
final NioTcpSession session = new NioTcpSession(this, socketChannel, readWriteSelectorLoop, idleChecker);
socketChannel.configureBlocking(false);
// apply idle configuration
session.getConfig().setIdleTimeInMillis(IdleStatus.READ_IDLE, config.getIdleTimeInMillis(IdleStatus.READ_IDLE));
session.getConfig().setIdleTimeInMillis(IdleStatus.WRITE_IDLE,
config.getIdleTimeInMillis(IdleStatus.WRITE_IDLE));
// apply the default service socket configuration
Boolean keepAlive = config.isKeepAlive();
...
Boolean tcpNoDelay = config.isTcpNoDelay();
if (tcpNoDelay != null) {
session.getConfig().setTcpNoDelay(tcpNoDelay);
}
......
// add the session to the queue for being added to the selector
readWriteSelectorLoop.register(false, false, true, false, session, socketChannel, new RegistrationCallback() {
@Override
public void done(SelectionKey selectionKey) {
session.setSelectionKey(selectionKey);
session.setConnected();
}
});
idleChecker.sessionRead(session, System.currentTimeMillis());
idleChecker.sessionWritten(session, System.currentTimeMillis());
}
创建回话的主要逻辑是设置Session相关的数据, 设置完成后,如下代码
readWriteSelectorLoop.register(false, false, true, false, session, socketChannel, new RegistrationCallback() {
@Override
public void done(SelectionKey selectionKey) {
session.setSelectionKey(selectionKey);
session.setConnected();
}
});
将channel添加到selector上,注册事件为read操作,同时设置SelectionKey的attchment为 NioTcpSession。后续读写相关的处理,将由NioTcpSession的ready方法处理。
@Override
public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer,
final boolean write) {
if (connect) {
.... }
if (read) {
processRead(readBuffer);
}
if (write) {
processWrite(selectorLoop);
}
if (accept) {
throw new IllegalStateException("accept event should never occur on NioTcpSession");
}
}
关键点:我们将看到处理客户端链接使用的是 acceptSelectorLoop,处理用户Session的读写使用的是readWriteSelectorLoop,这样来说对于客户端的链接和Session之间的读写使用不同Selector处理。
5)readWriteSelectorLoop.register方法的末尾参数传入了匿名内部类,在done方法中回调Session的setConnected方法。
void setConnected() {
if (!isCreated()) {
throw new RuntimeException("Trying to open a non created session");
}
state = SessionState.CONNECTED;
if (connectFuture != null) {
connectFuture.complete(this);
// free some memory
connectFuture = null;
}
processSessionOpen();
}
该方法首先设置session的状态为已连接,然后调用processSessionOpen()方法,processSessionOpen()方法调用了父类的processSessionOpen()方法。具体如下:
public void processSessionOpen() {
if (IS_DEBUG) {
LOG.debug("processing session open event");
}
try {
for (IoFilter filter : chain) {
filter.sessionOpened(this);
}
IoHandler handler = getService().getIoHandler();
if (handler != null) {
IoHandlerExecutor executor = getService().getIoHandlerExecutor();
if (executor != null) {
// asynchronous event
executor.execute(new OpenEvent(this));
} else {
// synchronous call (in the I/O loop)
handler.sessionOpened(this);
}
}
} catch (RuntimeException e) {
processException(e);
}
}
该方法首先循环调用filters的sessionOpened方法处理,处理完成后获取处理业务逻辑的IoHandler,调用IoHandler的sessionOpened方法进行处理。