Java NIO

# 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();

????????????????}

????????????}

????????}

????}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末君躺,一起剝皮案震驚了整個濱河市峭判,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棕叫,老刑警劉巖林螃,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異俺泣,居然都是意外死亡疗认,警方通過查閱死者的電腦和手機(jī)完残,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來横漏,“玉大人谨设,你說我怎么就攤上這事“砑耄” “怎么了铝宵?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長华畏。 經(jīng)常有香客問我沈堡,道長,這世上最難降的妖魔是什么驹饺? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任祠乃,我火速辦了婚禮,結(jié)果婚禮上仑乌,老公的妹妹穿的比我還像新娘百拓。我一直安慰自己,他們只是感情好晰甚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布衙传。 她就那樣靜靜地躺著,像睡著了一般厕九。 火紅的嫁衣襯著肌膚如雪蓖捶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天扁远,我揣著相機(jī)與錄音俊鱼,去河邊找鬼。 笑死畅买,一個胖子當(dāng)著我的面吹牛并闲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谷羞,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼帝火,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了湃缎?” 一聲冷哼從身側(cè)響起购公,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雁歌,沒想到半個月后宏浩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡靠瞎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年比庄,在試婚紗的時候發(fā)現(xiàn)自己被綠了求妹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡佳窑,死狀恐怖制恍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情神凑,我是刑警寧澤净神,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站溉委,受9級特大地震影響鹃唯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瓣喊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一坡慌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧藻三,春花似錦洪橘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逗概,卻和暖如春弟晚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仗谆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工指巡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淑履,地道東北人隶垮。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像秘噪,于是被迫代替她去往敵國和親狸吞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • JAVA NIO基礎(chǔ) ...
    文思li閱讀 610評論 0 3
  • 概述 NIO主要有三大核心部分:Channel(通道)指煎,Buffer(緩沖區(qū)),Selector蹋偏。 傳統(tǒng)IO基于...
    時之令閱讀 3,680評論 0 8
  • 前言: 之前的文章《Java文件IO常用歸納》主要寫了Java 標(biāo)準(zhǔn)IO要注意的細(xì)節(jié)和技巧,由于網(wǎng)上各種學(xué)習(xí)途徑至壤,...
    androidjp閱讀 2,894評論 0 22
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API威始,可以替代標(biāo)準(zhǔn)的Java I...
    編碼前線閱讀 2,258評論 0 5
  • 【原文】香九齡,能溫席像街。孝于親黎棠,所當(dāng)執(zhí)晋渺。 【譯文】東漢人黃香,九歲時就知道孝敬父親脓斩,替父親暖被窩木西。這是每個孝順父母...
    鄒林杰閱讀 356評論 0 0