Chapter1 章节结构
NIO 特点 OIO 比较
ServerSocketChannel --> ServerSocket
SocketChannel --> Socket
Selector (多路复用)
SelectionKey
特点:一个服务员服务多个客户,主要通过Selector多路服务
代码:
package com.john.netty.learn.ch01.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioServer {
private Selector selector = null;
private int port;
public NioServer(int port) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 不堵塞
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
// 注册我感兴趣的事件, 这里 Accept 新的客户接入
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
this.port = port;
}
public void listen() throws IOException {
System.out.println("启动服务" + port);
while (true) {
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey selectionKey = it.next();
// 避免重复
it.remove();
handler(selectionKey);
}
}
}
private void handler(SelectionKey selectionKey) throws IOException {
//当触发Socket.accpet 事件时,即有新的客户连接
if (selectionKey.isAcceptable()) {
// Key 包含 serverSocketChannel
// Refer to serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
handlerAccept(selectionKey);
return;
}
if (selectionKey.isReadable()) {
handlerRead(selectionKey);
}
}
private void handlerRead(SelectionKey selectionKey) throws IOException {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer);
System.out.println("接收到消息:" + new String(buffer.array()));
buffer.flip();
socketChannel.write(buffer);
}
private void handlerAccept(SelectionKey selectionKey) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
// 获取新的客户端-与新客户端握手
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
System.out.println("一个新客户连入...");
// 注册SocketChannel 读的感兴趣
socketChannel.register(this.selector, SelectionKey.OP_READ);
}
public static void main(String[] args) throws IOException {
NioServer nioServer = new NioServer(2000);
nioServer.listen();
}
}
问题解答:
1. Selector.select();阻塞,那为什么说NIO是非阻塞的IO?
a. 一个Socket系统对于阻塞和非阻塞,主要关心的不是在Accept 、Select 上, 而是在 read socket 流的时候,是否堵塞。
b. selector.select(1000);不阻塞
selector.wakeup();也可以唤醒selector (select方法) -- 这个非常重要,再随后的Netty源码中将讲解
selector.selectNow();也可以立马返还
2、客户端关闭的时候会抛出异常,死循环
3、SelectionKey.OP_WRITE是代表什么意思?
OP_WRITE表示底层缓冲区是否有空间,是则响应返还true