Netty学习(三)介绍nio核心组件selector

265 阅读2分钟

一、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();
            }
        }
    }
}