一、Selector选择器
上面的这个图描述了NIO的模型:
- 一个线程对应一个选择器
- 一个选择器上可以绑定多个通道
- 上面的模型是非阻塞的或者说比较高效的原因:
- 事件驱动选择器工作,事件由通道发出。有事件时,选择器会忙事件;没事件时,选择器可以忙别的。因为有一个线程去跟踪选择器,避免了多线程上下文切换的开销
二、看下源码
public abstract class Selector implements Closeable {
//获得一个选择器
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
//这个集合中保存这与选择器相连的通道key
public abstract Set<SelectionKey> selectedKeys();
//获得与选择器相连且活动着的通道,一个阻塞的方法
public abstract int select() throws IOException;
//下面两个方法与上面方法的不同在与 有个时间限制
public abstract int select(long timeout)
throws IOException;
public abstract int selectNow() throws IOException;
}
三、写了一个简单的例子
package com.angliali.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
* nio的客户端
*/
public class NIOClient {
public static void main(String[] args) throws IOException {
//获得通道
SocketChannel socketChannel = SocketChannel.open();
//设置通道非阻塞
socketChannel.configureBlocking(false);
//尝试去连接服务端
if(!socketChannel.connect(new InetSocketAddress("127.0.0.1",6666))){
while (!socketChannel.finishConnect()){
System.out.println("连接需要时间,但是不耽误其他工作");
}
}
//待发送的数据
String str = "Hello";
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes());
//将数据写入缓冲区
socketChannel.write(byteBuffer);
//
System.in.read();
}
}
package com.angliali.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
* nio的服务端
*/
public class NioServer {
public static void main(String[] args) throws IOException {
//创建serversocketchannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//获得selector
Selector selector = Selector.open();
//绑定ip和端口
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1",6666));
//设置非阻塞
serverSocketChannel.configureBlocking(false);
//将channel注册到selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//
while(true){
//连接判断
if(selector.select(5000) == 0){
System.out.println("等待了1秒,没有连接");
continue;
}
//得到当前的通道
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
//
if(selectionKey.isAcceptable()){
System.out.println("当前有一个连接");
SocketChannel channel = serverSocketChannel.accept();
channel.configureBlocking(false);
channel.register(selector,SelectionKey.OP_CONNECT, ByteBuffer.allocate(1024));
}
if(selectionKey.isReadable()){
System.out.println("开始读取数据");
SocketChannel channel = (SocketChannel)selectionKey.channel();
ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
channel.read(buffer);
System.out.println("数据:"+new String(buffer.array()));
}
iterator.remove();
}
}
}
}