selector模型
使用一個線程去監控多個IO請求,如果哪一個IO數據準備完畢後就通知相應的線程來處理
select模型,它的基本原理是採用輪詢和遍歷的方式。也就是説,在客户端操作服務器時,會創建三種文件描述符,簡稱FD。分別是writefds(寫描述符)、readfds(讀描述符)和 exceptfds(異常描述符)
demo,讓主線程監聽IO事件然後進行處理
server
public class NioServerExample {
public static void main(String[] args) throws IOException, InterruptedException {
Selector selector = getSelector();
listen(selector);
}
public static Selector getSelector() throws IOException {
Selector selector = Selector.open();
//創建可選通道,設置非阻塞
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
//綁定通道到指定端口
ServerSocket socket = serverSocketChannel.socket();
socket.bind(new InetSocketAddress(8080));
//向selector註冊IO事件,首先註冊SelectionKey.OP_ACCEPT讓server accept監聽
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
return selector;
}
public static void listen(Selector selector) throws IOException, InterruptedException {
while (selector.select() > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
if (iterator.hasNext()) {
SelectionKey key = iterator.next();
//判斷IO事件類型進行處理
process(selector, key);
iterator.remove();
}
}
}
private static void process(Selector selector, SelectionKey key) throws IOException, InterruptedException {
if (key.isAcceptable()) {
System.out.println(Thread.currentThread().getName()+" =>事件類型 accept");
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
System.out.println(Thread.currentThread().getName()+" =>事件類型 read");
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(500);
int len = channel.read(byteBuffer);
if (len > 0) {
String content = new String(byteBuffer.array(), 0, len);
System.out.println(content);
channel.register(selector, SelectionKey.OP_WRITE);
}
byteBuffer.clear();
} else if (key.isWritable()) {
System.out.println(Thread.currentThread().getName()+" =>事件類型 write");
SocketChannel channel = (SocketChannel) key.channel();
String str = "client fuck you I am Nio Server";
channel.write(ByteBuffer.wrap(str.getBytes()));
channel.register(selector, SelectionKey.OP_READ);
// channel.close(); //向客户端發送數據後管別此通道連接
}
}
}
client
public class NioClientExample0 {
public static void main(String[] args) throws IOException, InterruptedException {
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_WRITE);
String info = null;
Scanner scanner = new Scanner(System.in);
ByteBuffer byteBuffer0 = ByteBuffer.allocate(100);
ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
while (true) {
int len = socketChannel.read(byteBuffer1);
System.out.println(new String(byteBuffer1.array(), 0, len));
byteBuffer1.clear();
info = scanner.next();
byteBuffer0.put(info.getBytes());
byteBuffer0.flip();
socketChannel.write(byteBuffer0);
byteBuffer0.clear();
}
}
}
參考
舉例如下,開啓服務端和一個客户端後,用客户端向服務端發送一條數據
當服務端接收到數據並回寫給客户端後,又被註冊上讀事件,準備接收數據
這時再開啓一個客户端,會發現服務端沒有一直等待讀事件的IO,而是轉身處理accept接收新的連接去了