抛开Netty,一个典型的Java NIO服务端开发需要做几件事:
1. 创建ServerSocketChannel,设置为非阻塞,并绑定端口
2. 创建Selector对象
3. 给ServerSocketChannel注册SelectionKey.OP_ACCEPT事件
4. 启动一个线程循环,调用Selector的select方法来检查IO就绪事件,一旦有IO就绪事件,就通知用户线程去处理IO事件
5. 如果有Accept事件,就创建一个SocketChannel,并注册SelectionKey_OP_READ
6. 如果有读事件,判断一下是否全包,如果全包,就交给后端线程处理
7. 写事件比较特殊。isWriteable表示的是本机的写缓冲区是否可写。这个在绝大多少情况下都是为真的。在Netty中只有写半包的时候才需要注册写事件,如果一次写就完全把数据写入了缓冲区就不需要注册写事件. 具体关于写事件的处理看这篇 http://blog.csdn.net/iter_zc/article/details/39291129
一个典型的Java NIO服务端的代码如下:
public NIOServer(int port) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(port);
selector = Selector.open();
// 注册到selector,等待连接
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
private void eventloop() throws IOException {
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
handleSelectedKey(selectionKey);
}
}
}
private void handleKey(SelectionKey selectionKey) throws IOException {
ServerSocketChannel server = null;
SocketChannel client = null;
if (selectionKey.isAcceptable()) {
server = (ServerSocketChannel) selectionKey.channel();
client = server.accept();
// 配置为非阻塞
client.configureBlocking(false);
// 注册到selector,等待连接
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
client = (SocketChannel) selectionKey.channel();
client.read(receivebuffer);
} else if (selectionKey.isWritable()) {
sendbuffer.clear();
client = (SocketChannel) selectionKey.channel();
client.write(sendbuffer);
}
}
那么Netty是如何来注册Accept和读写事件的呢? 首先来看如何注册的OP_ACCPET事件,是在服务器端绑定端口的过程中的,具体的绑定细节看这篇 http://blog.csdn.net/iter_zc/article/details/39349177 这里只分析注册OP_ACCEPT的过程。
在ServerBootstrap调用createChannel来创建NioServerSocketChannel的时候,会给NioServerSocketChannel传入要绑定的OP_ACCEPT事件,然后依次调用父类的构造函数,把readInterestOp往上传递,直到传递到AbstractNioChannel,作为AbstractNioChannel的一个实例属性readInterestOp.
public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childGroup) {
super(null, eventLoop, childGroup, newSocket(), SelectionKey.OP_ACCEPT);
config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());
}
protected AbstractNioMessageServerChannel(
Channel parent, EventLoop eventLoop, EventLoopGroup childGroup, SelectableChannel ch, int readInterestOp) {
super(parent, eventLoop, ch, readInterestOp);
this.childGroup = childGroup;
}
protected AbstractNioMessageChannel(
Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {
super(parent, eventLoop, ch, readInterestOp);
}
protected AbstractNioChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {
super(parent, eventLoop);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
可以看到在AbstractNioChannel的构造函数里面并没有把Channel注册到selector之上,只是把OP_ACCEPT赋值给了readInterestOp。在服务器绑定的过程中有一个reg