# Java NIO #
Java NIO屬于非阻塞IO,這是與傳統(tǒng)IO最本質(zhì)的區(qū)別纳像。傳統(tǒng)IO包括socket和文件IO都是阻塞式的荆烈,即一個動作的執(zhí)行,必須等待先前的動作完成竟趾;非阻塞的IO在線程執(zhí)行一個動作憔购,不用等待動作執(zhí)行完,可以去做別的事情岔帽,這是因?yàn)镹IO是基于Channel的玫鸟,而不是基于流的。每個線程可以同時監(jiān)聽多個注冊到Selector上的Channel犀勒,???NIO 是一種同步非阻塞的 IO 模型鞋邑。同步是指線程不斷輪詢 IO 事件是否就緒诵次,非阻塞是指線程在等待 IO 的時候,可以同時做其他任務(wù)枚碗。同步的核心就是 Selector逾一,Selector 代替了線程本身輪詢 IO 事件,避免了阻塞同時減少了不必要的線程消耗肮雨;非阻塞的核心就是通道和緩沖區(qū)遵堵,當(dāng) IO 事件就緒時,可以通過寫到緩沖區(qū)怨规,保證 IO 的成功陌宿,而無需線程阻塞式地等待。
## Buffer 緩沖區(qū) ##
Java NIO中所有的緩沖區(qū)都繼承于 Buffer這個抽象類波丰。Buffer類似于一塊可以被讀寫的區(qū)域壳坪,因此有讀模式和寫模式兩種模式,在Buffer類中通過三個變量控制緩沖區(qū)的讀和寫position(下一個讀或?qū)懙臄?shù)組下標(biāo))掰烟,limit(在讀模式和寫模式下有不同的含義)爽蝴,capacity(數(shù)組的容量):
-?寫模式:往數(shù)組中寫入數(shù)據(jù)時候,將limit設(shè)置為capacity纫骑,表示最多可以寫入capacity個元素蝎亚,position就表示下一個寫入的下標(biāo)。通過Buffer的flip()方法可以從寫模式切換到讀模式先馆。
????????public final Buffer flip() {
????????????limit = position;
????????????position = 0;
????????????mark = -1;
????????????return this;
????????}
-?讀模式:從數(shù)組中讀取數(shù)據(jù)的時候发框,將limit設(shè)置為position,表示最多只有這么多個元素可以讀煤墙,position設(shè)置為0梅惯,表示從頭開始讀數(shù)據(jù)。通過調(diào)用Buffer的clear()方法或者compact()方法可以從讀模式切換到寫模式仿野。
????????public final Buffer clear() {
????????????position = 0;
????????????limit = capacity;
????????????mark = -1;
????????????return this;
????????}
clear()方法是不保留數(shù)據(jù)的(即使有些數(shù)據(jù)還沒有讀完)个唧,因?yàn)橹苯訉osition設(shè)置為0,表示從頭開始寫入數(shù)據(jù)设预,limit重新設(shè)置capacity。
????????public ByteBuffer compact() {
????????????System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
????????????position(remaining());
????????????limit(capacity());
????????????discardMark();
????????????return this;
????????}
compact()方法不是Buffer類中的方式犁河,是在Buffer的各個子類(ByteBuffer鳖枕、CharBuffer等)中定義的抽象方法,并且在(ByteBuffer桨螺、CharBuffer等)的子類中提供了實(shí)現(xiàn)宾符,compact()方法會保留還沒讀的數(shù)據(jù),先將沒讀的數(shù)據(jù)拷貝到數(shù)組的最前面灭翔,然后設(shè)置position為下一個寫入的下標(biāo)魏烫,limit重寫設(shè)置為capacity。
## ByteBuffer 字節(jié)緩沖區(qū) ##
ByteBuffer繼承于Buffer,ByteBuffer仍然是個抽象類哄褒,我們只能通過它的allocate(int capacity)方法獲取一個非直接字節(jié)緩沖區(qū)(緩沖區(qū)不是直接在內(nèi)存中的)稀蟋,也可以通過warp(byte[] b)方法創(chuàng)建一個非直接字節(jié)緩沖區(qū)。如果想要創(chuàng)建一個直接字節(jié)緩沖區(qū)呐赡,可以使用allocateDirect(int capacity)方法退客,直接緩沖區(qū)不是在堆上分配的,因此不受GC的管理链嘀,其創(chuàng)建和釋放過程比較耗時萌狂,但是直接緩沖區(qū)上數(shù)據(jù)的讀寫比較快。
????//在堆上創(chuàng)建一個字節(jié)緩沖區(qū)
????public static ByteBuffer allocate(int capacity) {
????????if (capacity < 0)
????????????throw new IllegalArgumentException();
????????return new HeapByteBuffer(capacity, capacity);
????}
????//創(chuàng)建一個直接字節(jié)緩沖區(qū)
????public static ByteBuffer allocateDirect(int capacity) {
????????return new DirectByteBuffer(capacity);
????}
**HeapByteBuffer:**HeapByteBuffer是 ByteBuffer的默認(rèn)實(shí)現(xiàn)類怀泊,在堆上創(chuàng)建一個字節(jié)緩沖區(qū).HeapByteBuffer主要有以下幾個方面的功能:
-?讀數(shù)據(jù):
????????//獲取position處的元素茫藏,并將position++
????????public byte get() {
????????????return hb[ix(nextGetIndex())];
????????}
????????//獲取指定下標(biāo)處的元素,此時position是沒有變的霹琼。
????????public byte get(int i) {
????????????return hb[ix(checkIndex(i))];
????????}
????????//獲取從position開始的length個元素务傲,并拷貝到指定數(shù)組中,position會更新為position + length
????????public ByteBuffer get(byte[] dst, int offset, int length) {
????????????checkBounds(offset, length, dst.length);
????????????if (length > remaining())
????????????????throw new BufferUnderflowException();
????????????System.arraycopy(hb, ix(position()), dst, offset, length);
????????????position(position() + length);
????????return this;
????}
-?寫數(shù)據(jù):
????????//將指定元素插入到position處碧囊,并將position++(如果滿了將會拋出異常)
????????public ByteBuffer put(byte x) {
????????????????hb[ix(nextPutIndex())] = x;
????????????????return this;
????????}
????????//將指定元素插入到指定位置树灶,position位置不會改變
?????????public ByteBuffer put(int i, byte x) {
????????????hb[ix(checkIndex(i))] = x;
????????????return this;
????????}
????????//從position處開始寫入指定數(shù)組中的元素,position會被更新為(position + length)
????????public ByteBuffer put(byte[] src, int offset, int length) {
????????????checkBounds(offset, length, src.length);
????????????if (length > remaining())
????????????????throw new BufferOverflowException();
????????????System.arraycopy(src, offset, hb, ix(position()), length);
????????????position(position() + length);
????????????return this;
????????}
-?讀寫char, int ,short, double等其他基本數(shù)據(jù)類型
????????//獲取一個字符糯而,Bits類根據(jù)大端還是小端用不同的方式組合兩個字節(jié)
????????public char getChar() {
????????????return Bits.getChar(this, ix(nextGetIndex(2)), bigEndian);
????????}
????????//大端存儲模式天通,高位字節(jié)保存在低字節(jié)部分,因此組合的時候低字節(jié)在前面熄驼,高字節(jié)在后面
????????static char getCharB(ByteBuffer bb, int bi) {
????????????return makeChar(bb._get(bi????),
????????????????????????bb._get(bi + 1));
????????}
????????//小端存儲模式像寒,高位字節(jié)保存在高字節(jié)部分,因此組合的時候高字節(jié)在前面瓜贾,低字節(jié)在后面
????????static char getCharL(ByteBuffer bb, int bi) {
????????????return makeChar(bb._get(bi + 1),
????????????????????????bb._get(bi????));
????????}
????????//寫入一個char字符的時候也是一樣
????????public ByteBuffer putChar(char x) {
????????????Bits.putChar(this, ix(nextPutIndex(2)), x, bigEndian);
????????????return this;
????????}
-?用ByteBuffer包裝成其他基本數(shù)據(jù)類型的緩沖區(qū)(CharBuffer诺祸、IntBuffer等)
????????//ByteBuffer的asCharBuffer()方法
????????public CharBuffer asCharBuffer() {
????????????int size = this.remaining() >> 1;
????????????int off = offset + position();
????????????return (bigEndian
????????????????????? (CharBuffer)(new ByteBufferAsCharBufferB(this,
???????????????????????????????????????????????????????????????????-1,
???????????????????????????????????????????????????????????????????0,
???????????????????????????????????????????????????????????????????size,
???????????????????????????????????????????????????????????????????size,
???????????????????????????????????????????????????????????????????off))
????????????????????: (CharBuffer)(new ByteBufferAsCharBufferL(this,
???????????????????????????????????????????????????????????????????-1,
???????????????????????????????????????????????????????????????????0,
???????????????????????????????????????????????????????????????????size,
???????????????????????????????????????????????????????????????????size,
???????????????????????????????????????????????????????????????????off)));
????????}
asCharBuffer()方法也會根據(jù)機(jī)器是大端模式還是小端模式,創(chuàng)建不同的對象祭芦。如果是大端模式筷笨,將會創(chuàng)建ByteBufferAsCharBufferB類對象,由于ByteBufferAsCharBufferB對象是由ByteBuffer對象包裝而來的龟劲,雖然ByteBufferAsCharBufferB的父類是CharBuffer胃夏,但是ByteBufferAsCharBufferB類中操作的并不是字符數(shù)組而是字節(jié)數(shù)組,所以ByteBufferAsCharBufferB類對象在讀寫的時候仍然借助Bits類完成昌跌,即先通過字節(jié)組成字符再讀寫仰禀。根據(jù)ByteBuffer得到其他類型的緩沖區(qū)也是一樣的實(shí)現(xiàn)原理。
-?創(chuàng)建只讀型緩沖區(qū)
????????public ByteBuffer asReadOnlyBuffer() {
????????????return new HeapByteBufferR(hb,
?????????????????????????????????????????this.markValue(),
?????????????????????????????????????????this.position(),
?????????????????????????????????????????this.limit(),
?????????????????????????????????????????this.capacity(),
?????????????????????????????????????????offset);
????????}
asReadOnlyBuffer()會創(chuàng)建HeapByteBufferR類對象蚕愤,每種緩沖區(qū)都可以創(chuàng)建對應(yīng)的只讀型緩沖區(qū)答恶,只讀型緩沖區(qū)就直接繼承創(chuàng)建它的這個類饺蚊,如HeapByteBufferR繼承于HeapByteBuffer,在HeapByteBufferR類中重寫所有的put方法悬嗓,所有的put方法都直接拋出異常污呼,而get方法仍然使用父類的get方法。
**MappedByteBuffer**繼承于ByteBuffer烫扼,是直接字節(jié)數(shù)組的抽象父類曙求,在MappedByteBuffer類中只定義了三個和物理磁盤相關(guān)的方法:
-?isLoaded():用于判斷這個字節(jié)數(shù)組是否存在物理磁盤上
-?load():將直接字節(jié)數(shù)組中的數(shù)據(jù)寫到物理磁盤上
-?force():強(qiáng)制將直接字節(jié)數(shù)組中的數(shù)據(jù)寫到物理磁盤上
**DirectByteBuffer**繼承于MappedByteBuffer,用于創(chuàng)建直接字節(jié)數(shù)組的類映企。由于直接字節(jié)數(shù)組不是在堆上分配內(nèi)存悟狱,因此不受GC控制,創(chuàng)建和釋放過程比較繁瑣堰氓,通過通過Unsafe類的allocateMemory()方法分配內(nèi)存空間挤渐,而且DirectByteBuffer的所有g(shù)et()和put()方法都是通過Unsafe類完成
????//讀取position處的元素
????public byte get() {
????????return ((unsafe.getByte(ix(nextGetIndex()))));
????}
????//在position處寫入元素
????public ByteBuffer put(byte x) {
????????unsafe.putByte(ix(nextPutIndex()), ((x)));
????????return this;
????}
## Channel ##
關(guān)于同步、異步双絮、阻塞和非阻塞的區(qū)別:同步和異步說的是消息的通知機(jī)制浴麻,阻塞非阻塞說的是線程的狀態(tài) 。
-?同步阻塞IO:client在調(diào)用read()方法時囤攀,stream里沒有數(shù)據(jù)可讀软免,線程停止向下執(zhí)行,直至stream有數(shù)據(jù)焚挠。
-?同步非阻塞IO:client在調(diào)用read()方法時膏萧,stream里沒有數(shù)據(jù)可讀,read()方法就返回了蝌衔,線程可以去干別的事榛泛,但是需要有一個線程監(jiān)聽這stream中是否有數(shù)據(jù)準(zhǔn)備好。
-?異步非阻塞IO:服務(wù)端調(diào)用read()方法噩斟,若stream中無數(shù)據(jù)則返回曹锨,程序繼續(xù)向下執(zhí)行。當(dāng)stream中有數(shù)據(jù)時剃允,操作系統(tǒng)會負(fù)責(zé)把數(shù)據(jù)拷貝到用戶空間沛简,然后通知這個線程,這里的消息通知機(jī)制就是異步斥废!
NIO 是一種同步非阻塞的 IO 模型椒楣。同步是指線程不斷輪詢 IO 事件是否就緒,非阻塞是指線程在等待 IO 的時候营袜,可以同時做其他任務(wù)。同步的核心就是 Selector丑罪,Selector 代替了線程本身輪詢 IO 事件荚板,避免了阻塞同時減少了不必要的線程消耗凤壁;非阻塞的核心就是通道和緩沖區(qū),當(dāng) IO 事件就緒時跪另,可以通過寫道緩沖區(qū)拧抖,保證 IO 的成功,而無需線程阻塞式地等待免绿。
**Channel**:是NIO中通道的接口類唧席,只提供了兩個抽象方法,isOpen()判斷通道是否打開嘲驾,close()關(guān)閉一個通道淌哟。
**AbstractInterruptibleChannel**:是一個抽象類,實(shí)現(xiàn)了Channel接口辽故。任何一個通道如果想要實(shí)現(xiàn)在中斷時實(shí)現(xiàn)異步關(guān)閉通道徒仓,那么必須繼承這個類,這主要體現(xiàn)在兩個方面:
-?當(dāng)某個線程阻塞在channel上誊垢,而另一個線程調(diào)用了channel的close()方法掉弛,那么阻塞的線程會收到AsynchronousCloseException
-?如果某個線程阻塞在channel上,另一個線程調(diào)用了阻塞線程的interrupt()方法喂走,那么阻塞線程會收到ClosedByInterruptException殃饿,并且通道會被關(guān)閉。
AbstractInterruptibleChannel抽象類中定義了一組協(xié)同方法begin()和end()方法來完成這兩個功能芋肠,因此當(dāng)線程執(zhí)行一個可能阻塞的IO操作時乎芳,必須把這個IO操作放在begin()方法和end()方法之間,才能實(shí)現(xiàn)channel的異步關(guān)閉业栅。
????protected final void begin() {
????????if (interruptor == null) {
????????????interruptor = new Interruptible() {
????????????????????public void interrupt(Thread target) {
????????????????????????synchronized (closeLock) {
????????????????????????????if (!open)
????????????????????????????????return;
????????????????????????????open = false;
????????????????????????????interrupted = target;
????????????????????????????try {
????????????????????????????????//收到中斷請求后會回調(diào)AbstractInterruptibleChannel類的close()方法關(guān)閉通道
????????????????????????????????AbstractInterruptibleChannel.this.implCloseChannel();
????????????????????????????} catch (IOException x) { }
????????????????????????}
????????????????????}};
????????}
????????blockedOn(interruptor);
????????Thread me = Thread.currentThread();
????????if (me.isInterrupted())
????????????interruptor.interrupt(me);
????}
????protected final void end(boolean completed)
????????throws AsynchronousCloseException
????{
????????blockedOn(null);
????????Thread interrupted = this.interrupted;
????????//中斷觸發(fā)器不為空秒咐,會拋出ClosedByInterruptException
????????if (interrupted != null && interrupted == Thread.currentThread()) {
????????????interrupted = null;
????????????throw new ClosedByInterruptException();
????????}
????????//觸發(fā)器為空,沒有中斷碘裕,但是在阻塞的過程中channel被關(guān)閉了携取,拋出AsynchronousCloseException
????????if (!completed && !open)
????????????throw new AsynchronousCloseException();
????}
**SelectableChannel**:是一個抽象類,繼承于AbstractInterruptibleChannel帮孔。需要注冊到Selector上的通道必須繼承這個類
-?public abstract int validOps();獲取這個通道支持的事件
-?public abstract boolean isRegistered();通道是否注冊到了某個Selector上
-?public abstract SelectionKey keyFor(Selector sel);這個通道在指定Selector上注冊的事件
-?public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;注冊這個通道到指定的Selector上
-?public abstract SelectableChannel configureBlocking(boolean block) throws IOException;修改這個通道為非阻塞或阻塞
-?public abstract boolean isBlocking();如果這個通道是阻塞模式雷滋,返回true
**AbstractSelectableChannel**:是一個抽象類,是SelectableChannel的基礎(chǔ)實(shí)現(xiàn)類文兢。在AbstractSelectableChannel類中定義了一個SelectionKey數(shù)組晤斩,記錄這個channel注冊到了哪些Selector上,定義了一個keyCount記錄這個channel注冊的次數(shù)姆坚。并且channel的最初模式設(shè)置為阻塞模式澳泵。
-?判斷這個channel是否注冊了:keyCount不為0就表示注冊了
????????public final boolean isRegistered() {
????????????synchronized (keyLock) {
????????????????return keyCount != 0;
????????????}
????????}
-?獲取這個通道在指定Selector上的注冊事件,給定Selector兼呵,在SelectionKey[]數(shù)組中查找Selector與指定Selector相等的SelectionKey
????????public final SelectionKey keyFor(Selector sel) {
????????????return findKey(sel);
????????}
-?將這個通道注冊到指定Selector上兔辅,如果給定的事件ops不是這個通道支持的事件validOps()將會拋出異常腊敲,而且阻塞的channel無法注冊到Selector上,具體在注冊的時候如果channel已經(jīng)注冊到了這個Selector上维苔,那么更新ops和附加信息碰辅,如果這個channel還沒有注冊到這個Selector上,將會調(diào)用Selector 類的register(SelectableChannel ch, int ops, Object o)方法完成注冊
????????public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException{
????????????synchronized (regLock) {
????????????????if (!isOpen())
????????????????????throw new ClosedChannelException();
????????????????if ((ops & ~validOps()) != 0)
????????????????????throw new IllegalArgumentException();
????????????????if (blocking)
????????????????????throw new IllegalBlockingModeException();
????????????????SelectionKey k = findKey(sel);
????????????????if (k != null) {
????????????????????k.interestOps(ops);
????????????????????k.attach(att);
????????????????}
????????????????if (k == null) {
????????????????????// New registration
????????????????????synchronized (keyLock) {
????????????????????????if (!isOpen())
????????????????????????????throw new ClosedChannelException();
????????????????????????k = ((AbstractSelector)sel).register(this, ops, att);
????????????????????????addKey(k);
????????????????????}
????????????????}
????????????????return k;
????????????}
????????}
-?關(guān)閉通道除了需要關(guān)閉通道之外介时,還需要把SelectionKey[]數(shù)組上的SelectionKey取消没宾,而SelectionKey類中的cancel()方法又會調(diào)用AbstractSelector中的cancel()方法,AbstractSelector類中的cancel()方法將這個SelectionKey加入到cancelledKeys集合中沸柔。
????????protected final void implCloseChannel() throws IOException {
????????????implCloseSelectableChannel();
????????????synchronized (keyLock) {
????????????????int count = (keys == null) ? 0 : keys.length;
????????????????for (int i = 0; i < count; i++) {
????????????????????SelectionKey k = keys[i];
????????????????????if (k != null)
????????????????????????k.cancel();
????????????????}
????????????}
????????}
-?判斷channel是否是阻塞模式循衰,只有非阻塞channel才能注冊到Selector上
????????public final boolean isBlocking() {
????????????synchronized (regLock) {
????????????????return blocking;
????????????}
????????}
-?修改channel的阻塞模式
????????public final SelectableChannel configureBlocking(boolean block) throws IOException {
????????????synchronized (regLock) {
????????????????if (!isOpen())
????????????????????throw new ClosedChannelException();
????????????????if (blocking == block)
????????????????????return this;
????????????????if (block && haveValidKeys())
????????????????????throw new IllegalBlockingModeException();
????????????????implConfigureBlocking(block);
????????????????blocking = block;
????????????}
????????????return this;
????????}
**SelectionKey**:每次channel注冊到一個Selector都會返回一個SelectionKey對象,因此SelectionKey描述的是一次注冊事件中channel和Selector之間的映射關(guān)系勉失。
-?public abstract SelectableChannel channel();獲取SelectionKey對應(yīng)的channel
-?public abstract Selector selector();獲取對應(yīng)SelectionKey對應(yīng)的channel
-?public abstract boolean isValid();這個SelectionKey是否有效
-?public abstract void cancel();把這個SelectionKey置為無效
-?public abstract int interestOps();獲取這個SelectionKey關(guān)注的事件
-?public abstract SelectionKey interestOps(int ops);設(shè)置這個SelectionKey關(guān)注的事件
-?public abstract int readyOps();這個SelectionKey關(guān)注的事件中就緒了的事件
-?public static final int OP_READ = 1 << 0;讀事件
-?public static final int OP_WRITE = 1 << 2;寫事件
-?public static final int OP_CONNECT = 1 << 3;channel連接到服務(wù)器的連接事件
-?public static final int OP_ACCEPT = 1 << 4;服務(wù)器準(zhǔn)備好了接受連接事件
-?channel上有讀事件就緒羹蚣、寫事件就緒(isWritable)、連接事件就緒(isConnectable)乱凿、接受連接事件就緒(isAcceptable)
????public final boolean isReadable() {
????????????return (readyOps() & OP_READ) != 0;
????????}
**AbstractSelectionKey**:是一個抽象類顽素,是SelectionKey的基礎(chǔ)實(shí)現(xiàn)類。一個channel注冊到一個Selector上返回一個SelectionKey徒蟆,這個SelectionKey初始就是有效的胁出。
-?取消一個SelectionKey,先將SelectionKey置為無效段审,然后調(diào)用Selector的cancel(SelectionKey key)方法完成具體的取消操作
????????public final void cancel() {
????????????synchronized (this) {
????????????????if (valid) {
????????????????????valid = false;
????????????????????((AbstractSelector)selector()).cancel(this);
????????????????}
????????????}
????????}
**SelectionKeyImpl**:SelectionKey的最終實(shí)現(xiàn)類全蝶,繼承于AbstractSelectionKey。在SelectionKeyImpl類中定義了int類型interestOps寺枉、int類型的readyOps抑淫,實(shí)現(xiàn)了SelectionKey抽象類中的interestOps()、readyOps()姥闪、interestOps(int ops)這三個方法始苇。
## Selector ##
Selector是NIO得以實(shí)現(xiàn)的核心模塊之一,NIO屬于同步非阻塞IO筐喳,同步的核心就是 Selector催式,Selector 代替了線程本身輪詢 IO 事件,避免了阻塞同時減少了不必要的線程消耗避归;
**Selector**:是一個抽象類荣月。提供了靜態(tài)方法open()創(chuàng)建一個Selector對象,open()方法是使用SelectorProvider類完成的梳毙。
-?public abstract boolean isOpen();這個Selector是否打開了
-?public abstract SelectorProvider provider();獲取創(chuàng)建這個Selector的SelectorProvider
-?public abstract Set keys();獲取這個Selector上所有的注冊的channel對應(yīng)的SelectionKey
-?public abstract Set selectedKeys();獲取這個Selector上所有就緒了的channel對應(yīng)的SelectionKey
-?public abstract int selectNow() throws IOException;非阻塞的執(zhí)行一次選擇操作哺窄,沒有就緒的channel就立即返回0,否則返回有多少個channel就緒了。
-?public abstract int select(long timeout) throws IOException;執(zhí)行一次選擇操作萌业,并且阻塞一段時間蔑担,在這段時間里,如果有channel就緒了咽白,將會返回有多少個channel就緒了,如果到達(dá)了指定時間還沒有channel就緒鸟缕,就返回0.
-?public abstract int select() throws IOException;阻塞執(zhí)行選擇操作晶框,一直阻塞到有channel就緒,然后返回有多少個channel就緒了
-?public abstract Selector wakeup();使阻塞在select()方法上的線程立即返回懂从。
????????Selector selector = Selector.open();
????????channel.configureBlocking(false);
????????SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
????????while(true) {
??????????int readyChannels = selector.select();
??????????if(readyChannels == 0) continue;
??????????Set selectedKeys = selector.selectedKeys();
??????????Iterator keyIterator = selectedKeys.iterator();
??????????while(keyIterator.hasNext()) {
????????????SelectionKey key = keyIterator.next();
????????????if(key.isAcceptable()) {
????????????????// a connection was accepted by a ServerSocketChannel.
????????????} else if (key.isConnectable()) {
????????????????// a connection was established with a remote server.
????????????} else if (key.isReadable()) {
????????????????// a channel is ready for reading
????????????} else if (key.isWritable()) {
????????????????// a channel is ready for writing
????????????}
????????????keyIterator.remove();
??????????}
????????}
**AbstractSelector**:是一個抽象類授段,是Selector的基礎(chǔ)實(shí)現(xiàn)類.
-?定義了一個boolean類型的open變量,初始化為true番甩,表示一個Selector創(chuàng)建時就為打開狀態(tài)侵贵。
-?定義了一個cancelledKeys集合,表示已經(jīng)取消的SelectionKey的集合缘薛。SelectionKey中的cancel()方法會調(diào)用AbstractSelector類的cancel()方法窍育,cancel()方法將SelectionKey加入到cancelledKeys集合中。
????????void cancel(SelectionKey k) {???????????????????????// package-private
????????????synchronized (cancelledKeys) {
????????????????cancelledKeys.add(k);
????????????}
????????}
-?定義了一個SelectorProvider對象宴胧,用于實(shí)現(xiàn)Selector類中的provider()方法漱抓。
-?定義了一個反注冊方法deregister(SelectionKey key),調(diào)用的是channel的方法恕齐,將key從SelectionKey[] keys數(shù)組中移除
????????protected final void deregister(AbstractSelectionKey key) {
????????????((AbstractSelectableChannel)key.channel()).removeKey(key);
????????}
-?定義了一組協(xié)同方法begin()和end()乞娄,與AbstractInterruptibleChannel類中協(xié)同方法類似,在一個可能阻塞的IO操作前使用begin()方法显歧,在IO操作之后使用end()方法仪或,但是這里的協(xié)同方法是為了在產(chǎn)生中斷之后使select()方法立即返回。
????????protected final void begin() {
????????????if (interruptor == null) {
????????????????interruptor = new Interruptible() {
????????????????????????public void interrupt(Thread ignore) {
????????????????????????????//產(chǎn)生中斷之后士骤,調(diào)用wakeup()方法喚醒select()方法
????????????????????????????AbstractSelector.this.wakeup();
????????????????????????}};
????????????}
????????????AbstractInterruptibleChannel.blockedOn(interruptor);
????????????Thread me = Thread.currentThread();
????????????if (me.isInterrupted())
????????????????interruptor.interrupt(me);
????????}
**SelectorImpl**:仍然是一個抽象類范删,繼承于AbstractSelector類,進(jìn)一步實(shí)現(xiàn)了Selector類敦间。
-?定義了一個keys集合瓶逃,用于實(shí)現(xiàn)Selector類的keys()方法,直接返回這個集合廓块。
-?定義了一個selectedKeys集合厢绝,用于實(shí)現(xiàn)Selector類的selectedKeys()方法,直接返回這個集合带猴。
-?將三個select()方法均委托給一個抽象方法昔汉,待子類進(jìn)一步實(shí)現(xiàn)。
????????//委托給lockAndDoSelect()方法
????????public int select(long timeout) throws IOException{
??????????????????if (timeout < 0)
??????????????????????throw new IllegalArgumentException("Negative timeout");
??????????????????return lockAndDoSelect((timeout == 0) ? -1 : timeout);
????????}
????????//調(diào)用上一個方法
????????public int select() throws IOException {
?????????????return select(0);
????????}
????????//委托給lockAndDoSelect()方法
????????public int selectNow() throws IOException {
?????????????return lockAndDoSelect(0);
????????}
????????//同步鎖,進(jìn)一步委托給doSelect(long time)這個抽象方法靶病。
????????private int lockAndDoSelect(long timeout) throws IOException {
??????????????synchronized (this) {
??????????????????if (!isOpen())
??????????????????????throw new ClosedSelectorException();
??????????????????synchronized (publicKeys) {
??????????????????????synchronized (publicSelectedKeys) {
??????????????????????????return doSelect(timeout);
??????????????????????}
??????????????????}
??????????????}
??????????}
-?進(jìn)一步實(shí)現(xiàn)了close()方法会通,但是沒有完全實(shí)現(xiàn),還是委托給一個抽象方法
//先調(diào)用wakeup()方法使select()方法立即返回娄周,然后同步鎖涕侈,調(diào)用抽象方法implClose()
????????public void implCloseSelector() throws IOException {
?????????????wakeup();
?????????????synchronized (this) {
?????????????????synchronized (publicKeys) {
?????????????????????synchronized (publicSelectedKeys) {
?????????????????????????implClose();
?????????????????????}
?????????????????}
?????????????}
?????????}
-?初步實(shí)現(xiàn)了register(channel, int ops, Object att)方法,委托給抽象方法implRegister(SelectionKey k)方法
????????protected final SelectionKey register(AbstractSelectableChannel ch, int ops, Object attachment){
?????????????if (!(ch instanceof SelChImpl))
?????????????????throw new IllegalSelectorException();
????????????//新建一個SelectionKey對象
?????????????SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
????????????//設(shè)置附加信息
?????????????k.attach(attachment);
?????????????synchronized (publicKeys) {
?????????????????implRegister(k);
?????????????}
????????????//設(shè)置感興趣事件
?????????????k.interestOps(ops);
?????????????return k;
?????????}
-?定義了一個方法處理cancelledKeys集合煤辨,委托給抽象方法implDereg(SelectionKey k)方法
???????????void processDeregisterQueue() throws IOException {
?????????????????// Precondition: Synchronized on this, keys, and selectedKeys
????????????????//獲取cancelledKeys集合
?????????????????Set cks = cancelledKeys();
?????????????????synchronized (cks) {
?????????????????????if (!cks.isEmpty()) {
?????????????????????????Iterator i = cks.iterator();
?????????????????????????while (i.hasNext()) {
?????????????????????????????SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
?????????????????????????????try {
?????????????????????????????????implDereg(ski);
?????????????????????????????} catch (SocketException se) {
?????????????????????????????????IOException ioe = new IOException("Error deregistering key");
?????????????????????????????????ioe.initCause(se);
?????????????????????????????????throw ioe;
?????????????????????????????} finally {
?????????????????????????????????i.remove();
?????????????????????????????}
?????????????????????????}
?????????????????????}
?????????????????}
?????????????}
## SelectorProvider ##
SelectorProvider為Selector裳涛、DatagramChannel、SocketChannel众辨、ServerSocketChannel端三、Pipe這些Selector和channel提供打開方法。
**SelectorProvider**:是一個抽象類
-?????public abstract DatagramChannel openDatagramChannel() throws IOException;打開UDP通信channel
-?????public abstract Pipe openPipe() throws IOException;打開一個管道
-?????public abstract AbstractSelector openSelector() throws IOException;打開一個Selector
-?public abstract ServerSocketChannel openServerSocketChannel() throws IOException;打開一個服務(wù)器socket channel
-?????public abstract SocketChannel openSocketChannel() throws IOException;打開一個TCP通信channel
**SelectorProviderImpl**:是一個抽象類鹃彻,是SelectorProvider的基礎(chǔ)實(shí)現(xiàn)類郊闯。
-?打開UDP通信channel,返回的是UDP channel的實(shí)現(xiàn)類對象蛛株。
?????????public DatagramChannel openDatagramChannel() throws IOException???{??
????????????return new DatagramChannelImpl(this);??
????????}??
-?打開TCP通信channel团赁,返回的是Socket channel的實(shí)現(xiàn)類對象。
????????public SocketChannel openSocketChannel() throws IOException??{??
????????????return new SocketChannelImpl(this);??
????????}??
-?打開TCP通信服務(wù)器端的channel谨履,返回的是ServerSocket channel的實(shí)現(xiàn)類對象然痊。
????????public ServerSocketChannel openServerSocketChannel() throws IOException??{??
????????????return new ServerSocketChannelImpl(this);??
????????}?
-?打開一個管道,返回的是pipe實(shí)現(xiàn)類對象屉符。
????????public Pipe openPipe() throws IOException??{??
????????????return new PipeImpl(this);??
????????}??
-?打開Selector的方法沒有實(shí)現(xiàn)剧浸,待子類實(shí)現(xiàn)。
**WindowsSelectorProvider **:SelectorProvider的最終實(shí)現(xiàn)類矗钟,繼承于SelectorProviderImpl唆香。實(shí)現(xiàn)了Selector的打開方法
-?打開Selector。調(diào)用的是Selector的最終實(shí)現(xiàn)類WindowsSelectorImpl吨艇,通過WindowsSelectorImpl的構(gòu)造函數(shù)返回一個Selector
????????public AbstractSelector openSelector() throws IOException {
??????????????return new WindowsSelectorImpl(this);
????????}
## 再看 channel ##
**FileChannel**:是一個抽象類躬它,繼承于AbstractInterruptibleChannel類,沒有繼承SelectableChannel东涡,即FileChannel無法注冊到Selector上冯吓。FileChannel也無法以非阻塞模式讀寫。通過阻塞方式對文件讀寫疮跑。
-?打開一個FileChannel组贺,一般通過傳統(tǒng)IO流獲取FileChannel。但是FileChannel類中也定義了open()函數(shù)打開一個FileChannel祖娘,getChannel()方法底層就是調(diào)用的FileChannel的open()方法
????????FileInputStream fis = new FileInputStream("C:\\mycode\\hello.txt");
????????FileChannel inChannel = fis.getChannel();
-?從FileChannel讀取數(shù)據(jù):public abstract int read(ByteBuffer dst) throws IOException;將通道中的數(shù)據(jù)讀出來,并寫道指定ByteBuffer中.FileChannel也支持分散寫,即寫到多個ByteBuffer中.
????????public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
-?向FileChannel寫數(shù)據(jù): public abstract int write(ByteBuffer src) throws IOException;將ByteBuffer緩沖區(qū)中的數(shù)據(jù)寫入到channel中.FileChannel也支持聚集寫,即多個ByteBuffer中的數(shù)據(jù)寫到channel中.
????????public abstract long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
-?在FileChannel的某個特定位置進(jìn)行數(shù)據(jù)的讀/寫操作,改變文件position的位置.
????????//獲取文件position的位置
????????public abstract long position() throws IOException;
????????//設(shè)置文件position
????????public abstract FileChannel position(long newPosition) throws IOException;
-?將channel中的數(shù)據(jù)寫入到另一個channel失尖,或?qū)⒘硪粋€channel中的數(shù)據(jù)寫入到這個channel中
????????//將本channel中的數(shù)據(jù)寫入到另一個“可寫入”的channel,從另一個channel的position處開始寫入
????????public abstract long transferTo(long position, long count, WritableByteChannel target) throws IOException;
????????//將另一個channel中的數(shù)據(jù)寫入到本channel中,從另一個channel的position處開始
????????public abstract long transferFrom(ReadableByteChannel src, long position, long count) throws IOException;
-?獲取channel關(guān)聯(lián)的文件的大小
????????public abstract long size() throws IOException;
-?截取一個指定大小的文件掀潮,截斷channel關(guān)聯(lián)的文件為size大小菇夸,size之后的數(shù)據(jù)會被丟棄
????????public abstract FileChannel truncate(long size) throws IOException;
**DatagramChannel**:是一個抽象類,真正的實(shí)現(xiàn)類是其子類DatagramChannelImpl仪吧,繼承于AbstractSelectableChannel類庄新。因此有阻塞和非阻塞兩種模式,在非阻塞模式下可以注冊到Selector上薯鼠。UDP通信的channel摄咆。
-?創(chuàng)建一個DatagramChannel。調(diào)用DatagramChannel的靜態(tài)方法open()人断,通過SelectorProvider去創(chuàng)建DatagramChannelImpl的實(shí)例
????????public static DatagramChannel open() throws IOException {
????????????return SelectorProvider.provider().openDatagramChannel();
????????}
-?DatagramChannel支持的事件:讀和寫
????????public final int validOps() {
????????????return (SelectionKey.OP_READ | SelectionKey.OP_WRITE);
????????}
-?報文流本來是無連接的,在沒有連接到一個指定地址時朝蜘,channel可以同時發(fā)送數(shù)據(jù)報到多個遠(yuǎn)程地址恶迈、也可以同時從多個遠(yuǎn)程地址接收數(shù)據(jù)報。通過DatagramChannel的send(ByteBuffer src, SocketAddress add)方法發(fā)送數(shù)據(jù)報到指定遠(yuǎn)程地址谱醇,通過DatagramChannel的receive(ByteBuffer dst)方法從任意遠(yuǎn)程地址接收數(shù)據(jù)報暇仲,receive()方法會返回一個SocketAddress對象用以標(biāo)識數(shù)據(jù)報來自哪個遠(yuǎn)程地址。在沒有建立連接的時候副渴,每一次調(diào)用send()方法發(fā)送數(shù)據(jù)報或者調(diào)用receive()方法接收數(shù)據(jù)報時都會接收安全檢查奈附。
????????//發(fā)送數(shù)據(jù)報到指定遠(yuǎn)程地址
????????public abstract int send(ByteBuffer src, SocketAddress target) throws IOException;
????????//從任意遠(yuǎn)程地址接收數(shù)據(jù)報,并返回數(shù)據(jù)來自哪個地址
????????public abstract SocketAddress receive(ByteBuffer dst) throws IOException;
-?報文流也可以建立連接煮剧。建立連接后斥滤,channel將只能從指定的遠(yuǎn)程地址接收數(shù)據(jù)報、同時也只能發(fā)送數(shù)據(jù)報到指定的遠(yuǎn)程地址勉盅。由于已經(jīng)連接到了指定的遠(yuǎn)程地址佑颇,因此在發(fā)送或者接收數(shù)據(jù)報的時候可以調(diào)用write()方法已經(jīng)read()方法。write(ByteBuffer src)方法將數(shù)據(jù)報發(fā)送到指定遠(yuǎn)程地址草娜、read(ByteBuffer dst)方法從指定遠(yuǎn)程地址接收數(shù)據(jù)報挑胸。指定連接到指定遠(yuǎn)程地址的channel才能調(diào)用write()方法和read()方法,每次調(diào)用write()方法和read()方法時不需要接收安全檢查宰闰。**將DatagramChannel置于已連接的狀態(tài)可以使除了它所“連接”到的地址之外的任何其他源地址的數(shù)據(jù)報被忽略茬贵。這是很有幫助的,因?yàn)椴幌胍陌家呀?jīng)被網(wǎng)絡(luò)層丟棄了移袍,從而避免了使用代碼來接收解藻、檢查然后丟棄包的麻煩。**
????????//將channel連接到指定遠(yuǎn)程地址
????????public abstract DatagramChannel connect(SocketAddress remote) throws IOException;
????????//斷開channel與遠(yuǎn)程地址間的連接
????????public abstract DatagramChannel disconnect() throws IOException;
????????//channel是否連接到了某個遠(yuǎn)程地址
????????public abstract boolean isConnected();
????????//發(fā)送數(shù)據(jù)報到指定遠(yuǎn)程地址葡盗,支持聚集寫
????????public abstract int write(ByteBuffer src) throws IOException;
????????public final long write(ByteBuffer[] srcs) throws IOException {
????????????return write(srcs, 0, srcs.length);
????????}
????????public abstract long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
????????//從指定遠(yuǎn)程地址接收數(shù)據(jù)報舆逃,支持分散讀
????????public abstract int read(ByteBuffer dst) throws IOException;
????????public final long read(ByteBuffer[] dsts) throws IOException {
????????????return read(dsts, 0, dsts.length);
????????}
????????public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
-?如果channel處于阻塞模式:調(diào)用send()方法或者write()方法,調(diào)用線程可能會休眠直到數(shù)據(jù)報被加入傳輸隊列。如果channel是非阻塞的:send()方法或者write()方法路狮、read()返回值要么是字節(jié)緩沖區(qū)的字節(jié)數(shù)虫啥,要么是“0”,receive()方法的返回值要么是遠(yuǎn)程地址對象要么是null奄妨。
-?報文流可以綁定也可以不綁定涂籽。如果channel負(fù)責(zé)監(jiān)聽,那么必須綁定到一個指定端口砸抛,channel將會一直監(jiān)聽這個端口评雌。當(dāng)channel沒有綁定的時候,仍然能夠接收數(shù)據(jù)報直焙,使用的是動態(tài)分配的端口號景东。已經(jīng)綁定的channel將從指定端口接收或者發(fā)送數(shù)據(jù)報。
-?報文流是不可靠傳輸奔誓。1)假如receive()方法提供的ByteBuffer沒有足夠的剩余空間來存放您正在接收的數(shù)據(jù)包斤吐,沒有被填充的字節(jié)都會被悄悄地丟棄。2)如果send()方法給定的ByteBuffer傳輸隊列沒有足夠空間來承載整個數(shù)據(jù)報厨喂,那么什么內(nèi)容都不會被發(fā)送和措。3)傳輸過程中的協(xié)議可能將數(shù)據(jù)報分解成碎片。例如蜕煌,以太網(wǎng)不能傳輸超過1,500個字節(jié)左右的包派阱。如果您的數(shù)據(jù)報比較大,那么就會存在被分解成碎片的風(fēng)險斜纪,成倍地增加了傳輸過程中包丟失的幾率贫母。被分解的數(shù)據(jù)報在目的地會被重新組合起來,接收者將看不到碎片盒刚。但是颁独,如果有一個碎片不能按時到達(dá),那么整個數(shù)據(jù)報將被丟棄伪冰。
**SocketChannel**:是一個抽象類誓酒,真正的實(shí)現(xiàn)類是其子類SocketChannelImpl,繼承于AbstractSelectableChannel類贮聂。因此有阻塞和非阻塞兩種模式靠柑,在非阻塞模式下可以注冊到Selector上∠判福客戶端的TCP通信的channel歼冰。
-?創(chuàng)建一個SocketChannel對象,通過SocketChannel的靜態(tài)方法open委托給SelectorProvider類創(chuàng)建SocketChannelImpl類對象耻警。
????????public static SocketChannel open() throws IOException {
????????????return SelectorProvider.provider().openSocketChannel();
????????}
-?SocketChannel支持的事件:讀隔嫡、寫甸怕、發(fā)起連接
????public final int validOps() {
????????return (SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT);
????}
-?channel必須在使用之前連接到遠(yuǎn)程地址,在阻塞模式下腮恩,連接操作會一直阻塞直到連接成功或者失斏液肌;在非阻塞模式下秸滴,連接操作即使沒有連接成功也會立刻返回武契,因此需要通過finishConnect()方法判斷是否連接成功并一直嘗試連接。
????????//channel是否成連接到一個遠(yuǎn)程地址
????????public abstract boolean isConnected();
????????//channel是否正處于連接中
????????public abstract boolean isConnectionPending();
????????//channel連接一個遠(yuǎn)程地址
????????public abstract boolean connect(SocketAddress remote) throws IOException;
????????//channel是否完成了連接
????????public abstract boolean finishConnect() throws IOException;
????????while(!channel.finishConnect()){
????????????//由于必須在連接成功之后才能進(jìn)行IO操作荡含,必須等待連接成功
????????????doSomethingElse();
????????}
-?往channel中寫數(shù)據(jù)咒唆,支持聚集寫。寫完之后释液,Selector的select()方法會檢測到這個channel的WRITE事件就緒了
????????public abstract int write(ByteBuffer src) throws IOException;
????????public final long write(ByteBuffer[] srcs) throws IOException {
????????????return write(srcs, 0, srcs.length);
????????}
????????public abstract long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
-?從channel中讀取數(shù)據(jù)全释,支持分散讀。Selector的select()方法會檢測到這個channel的READ事件就緒了
????????public abstract int read(ByteBuffer dst) throws IOException;
????????public final long read(ByteBuffer[] dsts) throws IOException {
????????????return read(dsts, 0, dsts.length);
????????}????
????????public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
SocketChannel的一個實(shí)例:
????????public class MyClient {
????????????private static Selector selector = null;
????????????private volatile static boolean stop = false;
????????????private static SocketChannel channel = null;
????????????public static void main(String[] args) {
????????????????selector = Selector.open();
????????????????try {
????????????????????channel = SocketChannel.open();
????????????????????channel.configureBlocking(false);
????????????????????channel.connect(new InetSocketAddress("127.0.0.1", 7777));
????????????????????//注冊一個連接請求事件
????????????????????channel.register(selector, SelectionKey.OP_CONNECT);
????????????????????try {
????????????????????????while (!stop) {
????????????????????????????selector.select();
????????????????????????????Set selectedKeys = selector.selectedKeys();
????????????????????????????Iterator iterator = selectedKeys.iterator();
????????????????????????????while (iterator.hasNext()) {
????????????????????????????????SelectionKey key = iterator.next();
????????????????????????????????//連接就緒
????????????????????????????????if (key.isConnectable()) {
????????????????????????????????????//服務(wù)器接收了連接請求
????????????????????????????????????SocketChannel sc = (SocketChannel) key.channel();
????????????????????????????????????if (sc.finishConnect()) {
????????????????????????????????????????// 將關(guān)注的事件變成read
????????????????????????????????????????sc.register(selector, SelectionKey.OP_READ);
????????????????????????????????????????doWrite(sc, "dddddd");
????????????????????????????????????}
????????????????????????????????}
????????????????????????????????// 讀就緒
????????????????????????????????if (key.isReadable()) {
????????????????????????????????????//服務(wù)器有數(shù)據(jù)過來了误债,處理數(shù)據(jù)浸船,再發(fā)送數(shù)據(jù)
????????????????????????????????}
????????????????????????????????iterator.remove();
????????????????????????????}
????????????????????????}
????????????????????} catch (IOException e) {
????????????????????????e.printStackTrace();
????????????????????}
????????????????} catch (ClosedChannelException e) {
????????????????????System.out.println("client: 失去主機(jī)連接");
????????????????????e.printStackTrace();
????????????????} catch (IOException e) {
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
**ServerSocketChannel**:是一個抽象類,真正的實(shí)現(xiàn)類是其子類ServerSocketChannelImpl找前,繼承于AbstractSelectableChannel類。因此有阻塞和非阻塞兩種模式判族,在非阻塞模式下可以注冊到Selector上躺盛。服務(wù)器端的TCP通信的channel。
-?創(chuàng)建一個ServerSocketChannel形帮,通過ServerSocketChannel的靜態(tài)方法open委托給SelectorProvider類創(chuàng)建ServerSocketChannelImpl類對象槽惫。
????????public static ServerSocketChannel open() throws IOException {
????????????return SelectorProvider.provider().openServerSocketChannel();
????????}
-?ServerSocketChannel支持的事件:接收連接
????????public final int validOps() {
????????????return SelectionKey.OP_ACCEPT;
????????}
-?ServerSocketChannel必須先綁定到一個端口上,一直監(jiān)聽這個端口辩撑。
????????public abstract ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException;
-?獲取與這個ServerSocketChannel關(guān)聯(lián)的SocketChannel界斜,即發(fā)起連接請求的SocketChannel
????????public abstract SocketChannel accept() throws IOException;
ServerSocketChannel的一個實(shí)例:
????public class MyService {
????????public static Selector selector = null;
????????public static void main(String[] args) {
????????????selector = Selector.open();// 打開selector
????????????ServerSocketChannel server = ServerSocketChannel.open();
????????????server.socket().bind(new InetSocketAddress(7777), 1024);
????????????server.configureBlocking(false);
????????????//服務(wù)器開始監(jiān)聽等待連接,注冊ACCEPT事件
????????????server.register(selector, SelectionKey.OP_ACCEPT);
????????????while (true) {
????????????????try {
????????????????????selector.select(1000); // 阻塞selector
????????????????????// ================如果有新連接
????????????????????Set selectedKeys = selector.selectedKeys();// 獲得事件集合;
????????????????????// ================遍歷selectedKeys
????????????????????Iterator iterator = selectedKeys.iterator();
????????????????????SelectionKey key = null;
????????????????????while (iterator.hasNext()) {
????????????????????????key = iterator.next();// 獲得到當(dāng)前的事件
????????????????????????// ===============處理事件
?????????????????????????// 連接就緒合冀,有客戶端請求連接各薇,注冊的事件發(fā)生
????????????????????????if (key.isAcceptable()) {
????????????????????????????// 獲得對應(yīng)的ServerSocketChannel
????????????????????????????ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
????????????????????????????// 得到對應(yīng)的SocketChannel?
????????????????????????????SocketChannel channel = ssc.accept();
????????????????????????????// 處理socketChannel
????????????????????????????channel.configureBlocking(false);?
????????????????????????????channel.register(selector, SelectionKey.OP_READ);?
????????????????????????}
????????????????????????// 讀就緒
????????????????????????if (key.isReadable()) {
????????????????????????????//客戶端有數(shù)據(jù)過來了,之前注冊的READ事件來了
????????????????????????}
????????????????????????// ===============
????????????????????????iterator.remove(); // 移除事件
????????????????????}
????????????????} catch (IOException e) {
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
????}