NIO主要有三大核心部分:Buffer(缓冲区),Channel(通道), Selector(选择器),本篇主要介绍后面两个。
nio常见方法
- ServerSocketChannelImpl是ServerSocketChannel的子类,ServerSocketChannel是AbstractSelectableChannel的子类,详细如下
SelectableChannel两个参数的register()方法默认调用AbstractSelectableChannel三个参数的register()方法,第一个参数是注册进Selector,第二个参数是感兴趣的事件,第三个参数附带一个对象,返回SelectionKey- 一个Selector可以被多个ServerSocketChannel注册
- 一个SelectableChannel可以注进不同的Selector
- 一个channel与一个selector对应一个selectionKey,所以可以用
channel.keyfor(selector)方法返回唯一的SelectionKey
ServerSocketChannel的accept()方法监听新加来的连接,若是非阻塞模式立即返回,阻塞模式下直到有连接时返回SocketChannelSelectionKey的channel()方法返回ServerSocketChannel或SocketChannelSelectionKey的selector()方法返回SelectorSelectionKey的attachment()方法返回附带的对象SelectionKey的interestOps()方法,注册时是什么,此处就返回什么,当然可以用isReadable、isWritable、isConnectable、isAcceptable来看看它具体对那些事件感兴趣SelectionKey的readyOps()方法来获取相关channel已经就绪的事件,它是interest集合的子集- SocketChannelImpl是SocketChannel的子类,SocketChannel是AbstractSelectableChannel的子类
Selector的select()方法返回已就绪的SelectionKey数量,selectNow()是立即返回,如果不设时间,将会阻塞直到有SelectionKey就绪,可以用selectedKeys()返回已就绪的SelectionKey的Set集合Selector的wakeup()方法可以唤醒一次因为select()带来的阻塞,如果正在阻塞则立即解除,否则解除下一次的阻塞
启动阶段
NioEndpoint->bind():
initServerSocket();
......
// 开启NioBlockingSelector.BlockPoller线程
selectorPool.open();
NioEndpoint->initServerSocket():
// 将会返回一个ServerSocketChannelImpl类
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
// 这个channel绑定的端口,可以通过server.port设置,默认8080端口
serverSock.socket().bind(addr,getAcceptCount());
// 设置为阻塞模式
serverSock.configureBlocking(true);
NioEndpoint内部封装NioSelectorPool,NioSelectorPool内部封装NioBlockingSelector,NioBlockingSelector内部有BlockPoller线程,该类以及线程的作用在这里。
SelectableChannel的注册
Poller在拉取事件时,调用了events方法,如果有事件的话,会将channel注入selector,所以才有后面run()方法中的select()大于0、遍历selectedKeys()不为空、以及processKey()的isReadable()返回为true的情况
NioEndpoint->Poller->events():
PollerEvent pe = null;
// 这里的pe是前面调用register()方法添加进来的
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
try {
pe.run();
// 调用reset方法之后,interestOps变为0
pe.reset();
......
NioEndpoint->PollerEvent->run():
// 前面register方法中,将interestOps值设为256,走if方法
if (interestOps == OP_REGISTER) {
// 这里调用完register方法之后,selector的keys数量增加1
socket.getIOChannel().register(
socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
} else {
final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
final NioSocketWrapper socketWrapper = (NioSocketWrapper) key.attachment();
if (socketWrapper != null) {
// 如果key不为空的话,追加当前设置的感兴趣的事件(此处仍为读)
int ops = key.interestOps() | interestOps;
socketWrapper.interestOps(ops);
key.interestOps(ops);
}