3. Java NIO之Selector如何實現(一)

在這裏插入圖片描述
咱們先捋一遍再看源碼:
Selector selector = Selector.open(); 在默認情況下生成了一個WindowsSelectorImpl實例,並且建立了Pipe
創建Selector對象:

Selector selector = Selector.open();

Selector實現原理:
SocketChannel、ServerSocketChannel和Selector的實例初始化都通過SelectorProvider類實現,我們先獲取相應的SelectorProvider。

public static SelectorProvider provider() {
    synchronized (lock) {
    //provider不爲空,直接返回provider
        if (provider != null)
            return provider;
        return AccessController.doPrivileged(
            new PrivilegedAction<SelectorProvider>() {
                public SelectorProvider run() {
                //由JDK的參數-Djava.nio.channels.spi.SelectorProvider=class設置的class來反射構造SelectorProvider
                        if (loadProviderFromProperty())
                            return provider;
                             //從jar中的目錄META-INF/services配置文件中找參數java.nio.channels.spi.SelectorProvider=class設置的第一個class來反射構造SelectorProvider
                        if (loadProviderAsService())
                            return provider;
                        provider = sun.nio.ch.DefaultSelectorProvider.create();
                        return provider;
                    }
                });
    }
}

AccessController.doPrivileged屬於特權操作,意思是不管這個方法由哪個用戶發起,都無需對此操作涉及的資源(文件讀寫特權等等)進行檢查。

由於SelectorProvider在不同操作系統下有不同的實現。
provider = sun.nio.ch.DefaultSelectorProvider.create();根據不同操作系統返回不同實現類,這裏主要以Windows實現梳理整個流程,windows平臺就返回WindowsSelectorProvider。

package sun.nio.ch;

import java.io.IOException;
import java.nio.channels.spi.AbstractSelector;

public class WindowsSelectorProvider extends SelectorProviderImpl {
    public WindowsSelectorProvider() {
    }

    public AbstractSelector openSelector() throws IOException {
        return new WindowsSelectorImpl(this);
    }
}

以默認情況爲例,採用的就是WindowsSelectorImpl.

在Windows操作系統JDK裏sun.nio.ch.DefaultSelectorProvider源代碼:

	package sun.nio.ch;
	import java.nio.channels.spi.SelectorProvider;
	public class DefaultSelectorProvider
	{
	  public static SelectorProvider create()
	  {
	    return new WindowsSelectorProvider();
	  }
	}

這裏直接返回WindowsSelectorProvider,即就是沒進行任何的配置,則會使用WindowsSelectorProvider,其構造方法爲空,沒有任何實現,獲得SelectorProvider後,調用openSelector()方法獲得對應Selector。

private final Pipe wakeupPipe = Pipe.open();
 
WindowsSelectorImpl(SelectorProvider var1) throws IOException {
    super(var1);
    this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal();
    SinkChannelImpl var2 = (SinkChannelImpl)this.wakeupPipe.sink();
    var2.sc.socket().setTcpNoDelay(true);
    this.wakeupSinkFd = var2.getFDVal();
    this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0);
}

觀察構造方法,首先需要用到wakeupPipe, 這個成員的取得需要Pipe調用open()方法。

public static Pipe open() throws IOException {
    return SelectorProvider.provider().openPipe();
}

直接調用了SelectorProvider的openPipe()方法:

public Pipe openPipe() throws IOException {
    return new PipeImpl(this);
}

openPipe()方法在WindowsSelectorProvider的父類SelectorProviderImpl類中實現,
openPipe()的實現調用了pipeImpl的構造方法,

static {
    Util.load();
    byte[] var0 = new byte[8];
    boolean var1 = IOUtil.randomBytes(var0);
    if(var1) {
        rnd = new Random(ByteBuffer.wrap(var0).getLong());
    } else {
        rnd = new Random();
    }
 
}
 
PipeImpl(SelectorProvider var1) throws IOException {
    try {
        AccessController.doPrivileged(new PipeImpl.Initializer(var1));
    } catch (PrivilegedActionException var3) {
        throw (IOException)var3.getCause();
    }
}

這裏首先在PipeImpl的靜態塊中會產生一個隨機數保存,然後在其構造方法中,通過AccessController調用doPrivileged方法new一個PipeImpl中內部類Initializer,這個方法會調用傳入的類所實現的run()方法,並保證其內部所涉及的權限問題,接下來看其run()方法:

     public Void run() throws IOException {
         PipeImpl.Initializer.LoopbackConnector var1 = new PipeImpl.Initializer.LoopbackConnector();
         var1.run();
         if(this.ioe instanceof ClosedByInterruptException) {
             this.ioe = null;
             Thread var2 = new Thread(var1) {
                 public void interrupt() {
                 }
             };
             var2.start();

             while(true) {
                 try {
                     var2.join();
                     break;
                 } catch (InterruptedException var4) {
                     ;
                 }
             }

             Thread.currentThread().interrupt();
         }

         if(this.ioe != null) {
             throw new IOException("Unable to establish loopback connection", this.ioe);
         } else {
             return null;
         }
     }

該方法內部會初始化Pipe管道的讀寫對象,初始化完成後對異常進行分類處理。啓動了一個線程,看LoopbackConnector(),內部類Initializer的內部類LoopbackConnector,通過LoopbackConnector的實例化對象調用其run()方法:

			private LoopbackConnector() {
            }

            public void run() {
            //定義服務端Channel
                ServerSocketChannel var1 = null;
                //定義兩個客戶端通道分別負責讀寫
                SocketChannel var2 = null;
                SocketChannel var3 = null;

                try {
                //初始化兩個ByteBuffer緩衝區,支持讀寫操作
                    ByteBuffer var4 = ByteBuffer.allocate(16);
                    ByteBuffer var5 = ByteBuffer.allocate(16);
                    InetAddress var6 = InetAddress.getByName("127.0.0.1");

                    assert var6.isLoopbackAddress();
//對服務端創建的IP和端口進行存儲,
                    InetSocketAddress var7 = null;
//自旋處理,直接成功初始化出讀寫對象
                    while(true) {
                    
            // 初始化ServerSocketChannel,用戶提供服務
                        if (var1 == null || !var1.isOpen()) {
                            var1 = ServerSocketChannel.open();
                            var1.socket().bind(new InetSocketAddress(var6, 0));
                            var7 = new InetSocketAddress(var6, var1.socket().getLocalPort());
                        }
//通過已經初始化的IP和端口打開一個寫通道
                        var2 = SocketChannel.open(var7);
                        PipeImpl.RANDOM_NUMBER_GENERATOR.nextBytes(var4.array());

                        do {
                        //將數據全部寫出去
                            var2.write(var4);
                        } while(var4.hasRemaining());

                        var4.rewind();
                        // 通過服務端獲取一個讀請求
        				// 此處讀取上一步寫的數據
                        var3 = var1.accept();

                        do {
                         // 讀取到寫數據, 添加到另一個緩衝區
                            var3.read(var5);
                        } while(var5.hasRemaining());

                        var5.rewind();
                        // 如果讀寫數據一致,說明管道通信正常,初始化管道的讀對象和寫對象
                        if (var5.equals(var4)) {
                        // 讀對象
                            PipeImpl.this.source = new SourceChannelImpl(Initializer.this.sp, var2);

               		 	// 寫對象 
                            PipeImpl.this.sink = new SinkChannelImpl(Initializer.this.sp, var3);
                            break;
                        }

                        var3.close();
                        var2.close();
                    }
                } catch (IOException var18) {
                    try {
                        if (var2 != null) {
                            var2.close();
                        }

                        if (var3 != null) {
                            var3.close();
                        }
                    } catch (IOException var17) {
                        ;
                    }

                    Initializer.this.ioe = var18;
                } finally {
                    try {
                        if (var1 != null) {
                            var1.close();
                        }
                    } catch (IOException var16) {
                        ;
                    }

                }

            }

val1通過ServerSocketChannel.open(),通過SelectorProviderImpl的openServerSocketChannel()

    public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
    }

返回ServerSocketChannelImpl

    ServerSocketChannelImpl(SelectorProvider var1) throws IOException {
        super(var1);
        this.fd = Net.serverSocket(true);
        this.fdVal = IOUtil.fdVal(this.fd);
        this.state = 0;
    }

回到WindowsSelectorImpl的構造方法,用wakeupSourceFd保存source(SourceChannelImpl)的fdVal值,用wakeupSinkFd保存sink(SinkChannelImpl)的fdVal值;禁用Nagle算法,最後使用pollWrpper成員保存source的fdVal值。得到source的fd跟sink的fd並保存,把wakeupSourceFd加到PoolWrapper中。至此selector創建完畢。

    WindowsSelectorImpl(SelectorProvider var1) throws IOException {
        super(var1);
        this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal();
        SinkChannelImpl var2 = (SinkChannelImpl)this.wakeupPipe.sink();
        var2.sc.socket().setTcpNoDelay(true);
        this.wakeupSinkFd = var2.getFDVal();
        this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0);
    }

    void addWakeupSocket(int var1, int var2) {
        this.putDescriptor(var2, var1);
        this.putEventOps(var2, Net.POLLIN);
    }    
   
    void putDescriptor(int var1, int var2) {
        this.pollArray.putInt(SIZE_POLLFD * var1 + 0, var2);
    }
 
    void putEventOps(int var1, int var2) {
        this.pollArray.putShort(SIZE_POLLFD * var1 + 4, (short)var2);
    }
發佈了27 篇原創文章 · 獲贊 4 · 訪問量 2997
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章