下面的只是对知识点的高度抽象,有些细节会有出入
BIO,NIO可以与Selector随意组合:BIO,NIO,BIO+Selector,NIO+Selector
| -selector | +selector |
---|
BIO | BIO | BIO+Selector | NIO | NIO | NIO+Selector |
IO模型使用不使用Selector区别在于:系统调用来获取有事件的channel的次数的不同
单独BIO,NIO因为要对IO进行访问,必然要系统调用,在用户层级就是可以维护一个List,每次有channel就往里面添加 ,之后不断遍历所有的channel来处理连接与读写请求等,这样每一次遍历每个通道获取事件都会去有次系统调用 组合BIO+Selector,NIO+Selector ,其中selector的作用就是通过一次系统调用去获取有事件的channel,这个功能是内核实现的,而不是像单独的版本是要多次系统调用,当然在组合的返回的有事件的还是需要你去一个个使用系统调用
总的来说:用了selector你就能使用一次系统调用获取有事件的channel,没有使用selector需要在用户层面多次使用系统调用获取有事件的channel
另外selector的poll与epoll模型区别最大在于:在内核模式下是否要对所有的channel全量的扫描。当然还有文件描述符的复制的不同
poll使用一次系统调用,在内核下会对每个channel进行扫描;epoll基于中断会把有事件的channel注册到一个空间去,一次系统调用直接就能拿到有事件的channel,不同poll需要扫描。 epoll的实现就是基于中断(增加补偿逻辑),红黑树(用于注册),链表(存放有事件的channel)实现,具体就不说了
下面这个代码只是演示我上面的思想
public class NIO{
public static void main(String[] args) throws Exception {
List<Channel> clients = new LinkedList<>();
ServerSocketChannel ss = ServerSocketChannel.open();
ss.bind(new InetSocketAddress(9090));
ss.configureBlocking(false);
clients.add(ss);
while (true) {
for (Channel c : clients) {
if(c instanceof ServerSocketChannel){
SocketChannel client = ss.accept();
if (client != null) {
System.out.println("处理连接");
client.configureBlocking(false);
clients.add(client);
}
}
ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
if(c instanceof SocketChannel){
int num = ((SocketChannel) c).read(buffer);
if (num > 0) {
buffer.flip();
byte[] aaa = new byte[buffer.limit()];
buffer.get(aaa);
System.out.println("处理读:"+ new String(aaa));
buffer.clear();
}
}
}
}
}
}
public class NIO_Selector{
private ServerSocketChannel server = null;
private Selector selector = null;
int port = 9090;
public void initServer() {
try {
server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(port));
server.configureBlocking(false);
selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
initServer();
System.out.println("服务器启动了....");
try {
while (true) {
Set<SelectionKey> keys = selector.keys();
System.out.println(keys.size()+" size");
while (selector.select() > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectionKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel client = ssc.accept();
client.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(8192);
client.register(selector, SelectionKey.OP_READ, buffer);
} else if (key.isReadable()) {
readHandler(key);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
com.bjmashibing.system.io.SocketMultiplexingSingleThreadv1 service = new com.bjmashibing.system.io.SocketMultiplexingSingleThreadv1();
service.start();
}
}
|