国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JAVA_NIO詳細解析說明

SillyMonkey / 1081人閱讀

摘要:通道是和選擇器一起被注冊的,并且使用選擇器來更新通道的就緒狀態。注冊不會立即被取消,但鍵會立即失效。這個集合的每個成員都是相關的通道被選擇器在前一個選擇操作中判斷為已經準備好的,并且包

Java NIO是一個用來替代標準Java IO API的新型數據傳遞方式,像現在分布式架構中會經常存在他的身影。其比傳統的IO更加高效,非阻塞,異步,雙向

NIO主體結構

Java NIO的主要構成核心就是Buffer、Channel和Selector這三個

對于Channel我想要提醒的是,Channel中的數據總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入

使用Selector,得向Selector注冊Channel,然后調用它的select()方法。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法返回,線程就可以處理這些事件

Channel

所有的 IO 在NIO 中都從一個Channel 開始。Channel 有點象流

Channel的實現

FileChannel:從文件中讀寫數據

DatagramChannel:通過UDP讀寫網絡中的數據

SocketChannel:通過TCP讀寫網絡中的數據

ServerSocketChannel:監聽新進來的TCP連接,像Web服務器那樣。對每一個新進來的連接都會創建一個SocketChannel

Scatter/Gather

分散(scatter)從Channel中讀取是指在讀操作時將讀取的數據寫入多個buffer中。因此,Channel將從Channel中讀取的數據“分散(scatter)”到多個Buffer中

聚集(gather)寫入Channel是指在寫操作時將多個buffer的數據寫入同一個Channel,因此,Channel 將多個Buffer中的數據“聚集(gather)”后發送到Channel

通過這樣的方式可以方便數據的讀取,當你想要獲取整個數據的一部分的時候,通過這種方式可以很快的獲取數據

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);

read()方法按照buffer在數組中的順序將從channel中讀取的數據寫入到buffer,當一個buffer被寫滿后,channel緊接著向另一個buffer中寫

transferFrom、transferTo

實現兩個Channel之間相互連接,數據傳遞

    public static void trainforNio() {
        RandomAccessFile fromFile=null;
        RandomAccessFile toFile=null;
        try {

            fromFile = new RandomAccessFile("src/nio.txt", "rw");
            // channel獲取數據
            FileChannel fromChannel = fromFile.getChannel();
            toFile = new RandomAccessFile("src/toFile.txt", "rw");
            FileChannel toChannel = toFile.getChannel();
            System.out.println(toChannel.size());
              //position處開始向目標文件寫入數據,這里是toChannel
            long position = toChannel.size();
            long count = fromChannel.size();
            toChannel.transferFrom(fromChannel, position, count);
            System.out.println(toChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fromFile != null) {
                    fromFile.close();
                }
                if (toFile != null) {
                    toFile.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

transferFrom、transferTo作用是一樣的,只是一個是tochannal調用,一個是fromchannnal調用

在實際的運用中可能存在源通道的剩余空間小于 count 個字節,則所傳輸的字節數要小于請求的字節數

在SoketChannel的實現中,SocketChannel只會傳輸此刻準備好的數據(可能不足count字節)。因此,SocketChannel可能不會將請求的所有數據(count個字節)全部傳輸到FileChannel中

看官一定要仔細看我栗子中的注釋

Buffer

Buffer是一個緩存區,其會將Channel中的數據存儲起來

Buffer的實現

ByteBuffer

CharBuffer

DoubleBuffer

FloatBuffer

IntBuffer

LongBuffer

ShortBuffer

MappedByteBuffer

capacity,position,limit

在講解該主題之前,首先要明白讀模式和寫模式,無論是Channel還是Buffer都存在這兩種模式,要理解這兩種模式,第一步要明確主題是哪一個,是Channel還是Buffer。舉個栗子,主角是Channel,讀模式的含義就是從Buffer中獲取數據,寫模式就是將數據寫入Buffer,對于Buffer則是相反。搞清楚這一點,理解下面的就要相對清楚一點

capacity:作為一個內存塊,其就代表了當前Buffer能最多暫存多少數據量,存儲的數據類型則是根據上面的Buffer對象類型,一旦Buffer滿了,需要將其清空(通過讀數據或者清除數據)才能繼續寫數據往里寫數據

position:代表當前數據讀或寫處于那個位置。讀模式:被重置從0開始,最大值可能為capacity-1或者limit-1,寫模式:被重置從0開始,最大值為limit-1

limit:最多能往Buffer里寫多少數據,limit大小跟數據量大小和capacity有關,讀模式:數據量>capacity時,limit=capacity,數據量=capacity時,limit=capacity,數據量

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Method {
    public static void nio() {
        RandomAccessFile aFile = null;
        try {

            aFile = new RandomAccessFile("src/nio.txt", "rw");
            // channel獲取數據
            FileChannel fileChannel = aFile.getChannel();
            // 初始化Buffer,設定Buffer每次可以存儲數據量
            // 創建的Buffer是1024byte的,如果實際數據本身就小于1024,那么limit就是實際數據大小
            ByteBuffer buf = ByteBuffer.allocate(1024);
            // channel中的數據寫入Buffer
            int bytesRead = fileChannel.read(buf);
            System.out.println(bytesRead);

            while (bytesRead != -1) {
                // Buffer切換為讀取模式
                buf.flip();
                // 讀取數據
                while (buf.hasRemaining()) {
                    System.out.print((char) buf.get());
                }
                // 清空Buffer區
                buf.compact();
                // 繼續將數據寫入緩存區
                bytesRead = fileChannel.read(buf);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (aFile != null) {
                    aFile.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Method.nio();
    
Buffer讀寫數據步驟

寫入數據到Buffer(fileChannel.read(buf))

調用flip()方法(buf.flip())

從Buffer中讀取數據(buf.get())

調用clear()方法或者compact()方法(buf.compact())

Buffer方法

flip():將Buffer讀模式切換到寫模式,并且將position制為0

clear():清空整個緩沖區

compact():只會清除已經讀過的數據。任何未讀的數據都被移到緩沖區的起始處,新寫入的數據將放到緩沖區未讀數據的后面

allocate(1024):初始化Buffer,設定的值就決定capacity值的大小

rewind():將position設回0,所以你可以重讀Buffer中的所有數據。limit保持不變,仍然表示能從Buffer中讀取多少個元素(byte、char等)

mark()與reset():通過調用Buffer.mark()方法,可以標記Buffer中的一個特定position。之后可以通過調用Buffer.reset()方法恢復到這個position

equals():當滿足下面三個條件時,兩個Buffer才是相等

有相同的類型(byte、char、int等)

Buffer中剩余的byte、char等的個數相等

Buffer中所有剩余的byte、char等都相同

只比較的是剩余的數據

compareTo():滿足下列條件,則認為一個Buffer“小于”另一個Buffer

第一個不相等的元素小于另一個Buffer中對應的元素

所有元素都相等,但第一個Buffer比另一個先耗盡(第一個Buffer的元素個數比另一個少)

Selector

Selector允許單線程處理多個 Channel。如果你的應用打開了多個連接(通道),但每個連接的流量都很低,使用Selector就會很方便

大致流程

當您調用一個選擇器對象的 select( )方法時,相關的鍵會被更新,用來檢查所有被注冊到該選擇器的通道。您可以獲取一個鍵的集合,從而找到當時已經就緒的通道。通過遍歷這些鍵,您可以選擇出每個從上次您調用 select( )開始直到現在,已經就緒的通道

選擇器(Selector)的特點
public abstract class Selector
{
// This is a partial API listing
public static Selector open( ) throws IOException
public abstract boolean isOpen( );//判斷是open
public abstract void close( ) throws IOException;//選擇鍵設置無效
public abstract SelectionProvider provider( );
}

選擇器類管理著一個被注冊的通道集合的信息和它們的就緒狀態。通道是和選擇器一起被注冊
的,并且使用選擇器來更新通道的就緒狀態。當這么做的時候,可以選擇將被激發的線程掛起,直

到有就緒的的通道

不能注冊已經關閉的selectableChannel

通過調用一個自定義的 SelectorProvider對象的 openSelector( )方法來創建一個 Selector 實例也是可行的。您可以通過調用 provider( )方法來決定由哪個 SelectorProvider 對象來創建給定的 Selector 實例

通道(Channel)的特點
public abstract class SelectableChannel
extends AbstractChannel
implements Channel
{
// This is a partial API listing
public abstract SelectionKey register (Selector sel, int ops)
throws ClosedChannelException;
public abstract SelectionKey register (Selector sel, int ops,
Object att)
throws ClosedChannelException;
public abstract boolean isRegistered( );
public abstract SelectionKey keyFor (Selector sel);
public abstract int validOps( );
}

繼承SelectableChannel

一個channel可以注冊到多個selector中

一個selector中同一個channel只能有一個

通道被注冊前,要非阻塞模式

支持Connect、Accept、Read、Write四種可選擇操作事件,但并不是所有的SelectableChannel都存在以上四類,可以通過validOps()獲取可以使用的操作事件集合

如果你對不止一種事件感興趣,那么可以用“位或”操作符將常量連接起來

任何一個通道和選擇器的注冊關系都被封裝在一個 SelectionKey 對象中。 keyFor( )方法將
返回與該通道和指定的選擇器相關的鍵。如果通道被注冊到指定的選擇器上,那么相關的鍵將被返

回。如果它們之間沒有注冊關系,那么將返回 null

選擇鍵(SelectionKey)的特點
package java.nio.channels;
public abstract class SelectionKey
{
public static final int OP_READ
public static final int OP_WRITE
public static final int OP_CONNECT
public static final int OP_ACCEPT
public abstract SelectableChannel channel( );
public abstract Selector selector( );
public abstract void cancel( );
public abstract boolean isValid( );
public abstract int interestOps( );
public abstract void interestOps (int ops);
public abstract int readyOps( );
public final boolean isReadable( )
public final boolean isWritable( )
public final boolean isConnectable( )
public final boolean isAcceptable( )
public final Object attach (Object ob)
public final Object attachment( )
}

封裝了特定的通道與特定的選擇器的注冊關系

一個 SelectionKey 對象包含兩個以整數形式進行編碼的byte掩碼:一個用于指示那些通道/
選擇器組合體所關心的操作(instrest 集合),另一個表示通道準備好要執行的操作( ready 集合)

當終結注冊關系時

當應該終結這種關系的時候,可以調用 SelectionKey對象的 cancel( )方法??梢酝ㄟ^調用 isValid( )方法來檢查它是否仍然表示一種有效的關系。當鍵被取消時,它將被放在相關的選擇器的已取消的鍵的集合里。注冊不會立即被取消,但鍵會立即失效。當再次調用 select( )方法時(或者一個正在進行的 select()調用結束時),已取消的鍵的集合中的被取消的鍵將被清理掉,并且相應的注銷也將完成。通道會被注銷,而新的SelectionKey 將被返回

當通道關閉時

當通道關閉時,所有相關的鍵會自動取消(記住,一個通道可以被注冊到多個選擇器上)。當
選擇器關閉時,所有被注冊到該選擇器的通道都將被注銷,并且相關的鍵將立即被無效化(取
消)。一旦鍵被無效化,調用它的與選擇相關的方法就將拋出 CancelledKeyException

interest 集合

當前的 interest 集合可以通過調用鍵對象的 interestOps( )方法來獲取

最初,這應該是通道被注冊時傳進來的值。這個 interset 集合永遠不會被選擇器改變,但您可以通過調用 interestOps( )方法并傳入一個新的byte掩碼參數來改變它。 interest 集合也可以通過將通道注冊到選擇器上來改變(實際上使用一種迂回的方式調用 interestOps( )),就像 4.1.2 小節中描的那樣。當相關的 Selector 上的 select( )操作正在進行時改變鍵的 interest 集合,不會影響那個正在進行的選擇操作。所有更改將會在 select( )的下一個調用中體現出來

ready集合

可以通過調用鍵的 readyOps( )方法來獲取相關的通道的已經就緒的操作。 ready 集合是 interest
集合的子集,并且表示了 interest 集合中從上次調用 select( )以來已經就緒的那些操作

SelectionKey 類定義了四個便于使用的布爾方法來為您測試這些byte值: isReadable( ), isWritable( ), isConnectable( ), 和 isAcceptable( )

SelectionKey 對象包含的 ready 集合與最近一次選擇器對所注冊的通道所作的檢查相同。而每個多帶帶的通道的就緒狀態會同時改變

附加的對象

可以將一個對象或者更多信息附著到SelectionKey上,這樣就能方便的識別某個給定的通道。例如,可以附加 與通道一起使用的Buffer,或是包含聚集數據的某個對象。使用方法如下:

selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();

還可以在用register()方法向Selector注冊Channel的時候附加對象。如:

SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);

如果選擇鍵的存續時間很長,但您附加的對象不應該存在那么長時間,請記得在完成后清理附件。否則,您附加的對象將不能被垃圾回收,您將會面臨內存泄漏問題

總體上說, SelectionKey 對象是線程安全的,但知道修改 interest 集合的操作是通過 Selector 對象進行同步的是很重要的。這可能會導致 interestOps( )方法的調用會阻塞不確定長的一段時間。選擇器所使用的鎖策略(例如是否在整個選擇過程中保持這些鎖)是依賴于具體實現的。幸好,這種多元處理能力被特別地設計為可以使用單線程來管理多個通道。被多個線程使用的選擇器也只會在系統特別復雜時產生問題。

選擇過程
public abstract class Selector
{
public abstract Set keys( );
public abstract Set selectedKeys( );
public abstract int select( ) throws IOException;
public abstract int select (long timeout) throws IOException;
public abstract int selectNow( ) throws IOException;
public abstract void wakeup( );
}

已注冊的鍵的集合

與選擇器關聯的已經注冊的鍵的集合。并不是所有注冊過的鍵都仍然有效。這個集合通過
keys( )方法返回,并且可能是空的。這個已注冊的鍵的集合不是可以直接修改的;試圖這么做的話
將引 java.lang.UnsupportedOperationException。

已選擇的鍵的集合

已注冊的鍵的集合的子集。這個集合的每個成員都是相關的通道被選擇器(在前一個選擇操作
中)判斷為已經準備好的,并且包含于鍵的 interest 集合中的操作。這個集合通過 selectedKeys( )方
法返回(并有可能是空的)

不要將已選擇的鍵的集合與 ready 集合弄混了。這是一個鍵的集合,每個鍵都關聯一個已經準
備好至少一種操作的通道。每個鍵都有一個內嵌的 ready 集合,指示了所關聯的通道已經準備好的
操作

鍵可以直接從這個集合中移除,但不能添加

已取消的鍵的集合

已注冊的鍵的集合的子集,這個集合包含了 cancel( )方法被調用過的鍵(這個鍵已經被無效
化),但它們還沒有被注銷。這個集合是選擇器對象的私有成員,因而無法直接訪問

在一個剛初始化的 Selector 對象中,這三個集合都是空的。

執行步驟

已取消的鍵的集合將會被檢查。如果它是非空的,每個已取消的鍵的集合中的鍵將從另外兩
個集合中移除,并且相關的通道將被注銷。這個步驟結束后,已取消的鍵的集合將是空的。

已注冊的鍵的集合中的鍵的 interest 集合將被檢查。在這個步驟中的檢查執行過后,對
interest 集合的改動不會影響剩余的檢查過程。

a.如果通道的鍵還沒有處于已選擇的鍵的集合中,那么鍵的 ready 集合將被清空,然后表示操
作系統發現的當前通道已經準備好的操作的比特掩碼將被設置。

b.否則,也就是鍵在已選擇的鍵的集合中。鍵的 ready 集合將被表示操作系統發現的當前已經
準備好的操作的比特掩碼更新。所有之前的已經不再是就緒狀態的操作不會被清除。事實上,所有的比特位都不會被清理。由操作系統決定的 ready 集合是與之前的 ready 集合按位分離的,一旦鍵被放置于選擇器的已選擇的鍵的集合中,它的 ready 集合將是累積的。比特位只會被設置,不會被清理。

步驟 2 可能會花費很長時間,特別是所激發的線程處于休眠狀態時。與該選擇器相關的鍵可
能會同時被取消。當步驟 2 結束時,步驟 1 將重新執行,以完成任意一個在選擇進行的過程中,鍵

已經被取消的通道的注銷。

select 操作返回的值是 ready 集合在步驟 2 中被修改的鍵的數量,而不是已選擇的鍵的集合中
的通道的總數。返回值不是已準備好的通道的總數,而是從上一個 select( )調用之后進入就緒狀態

的通道的數量。之前的調用中就緒的,并且在本次調用中仍然就緒的通道不會被計入,而那些在前
一次調用中已經就緒但已經不再處于就緒狀態的通道也不會被計入。這些通道可能仍然在已選擇的
鍵的集合中,但不會被計入返回值中。返回值可能是 0。

為什么延遲注銷

使用內部的已取消的鍵的集合來延遲注銷,是一種防止線程在取消鍵時阻塞,并防止與正在進
行的選擇操作沖突的優化。注銷通道是一個潛在的代價很高的操作,這可能需要重新分配資源(請
記住,鍵是與通道相關的,并且可能與它們相關的通道對象之間有復雜的交互)。

三種select()方法

僅僅在它們在所注冊的通道當前都沒有就緒時,是否阻塞的方面有所不同。

select():在沒有通道就緒時將無限阻塞。一旦至少有一個已注冊的通道就緒,選擇器的選擇鍵
就會被更新,并且每個就緒的通道的 ready 集合也將被更新。返回值將會是已經確定就緒的通道的

數目。正常情況下, 這些方法將返回一個零的值,因為直到一個通道就緒前它都會阻塞。

select(long timeout):如果在您提供的超時時間(以毫秒計算)內沒有通道就緒時,它將返回 0。如果一個或者多個通道在時間限制終止前就緒,鍵的狀態將會被更新,并且方法會在那時立即返回。將超時參數指定為 0 表示將無限期等待,那么它就在各個方面都等同于使用select()

selectNow():執行就緒檢查過程,但不阻塞。如果當前沒有通道就緒,它將立即返回 0

停止選擇過程

wakeUp()

某個線程調用select()方法后阻塞了,即使沒有通道已經就緒,也有辦法讓其從select()方法返回。只要讓其它線程在第一個線程調用select()方法的那個對象上調用Selector.wakeup()方法即可。阻塞在select()方法上的線程會立馬返回。

如果有其它線程調用了wakeup()方法,但當前沒有線程阻塞在select()方法上,下個調用select()方法的線程會立即“醒來(wake up)”。

close()

用完Selector后調用其close()方法會關閉該Selector,且使注冊到該Selector上的所有SelectionKey實例無效。通道本身并不會關閉。

interrupt()

如果睡眠中的線程的 interrupt( )方法被調用,它的返回狀態將被設置。如果被喚醒的線程之后
將試圖在通道上執行 I/O 操作,通道將立即關閉,然后線程將捕捉到一個異常。

例子

服務端

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOServer {
    // 通道管理器
    private Selector selector;

    public void initServer(int port) throws Exception {
        // 獲得一個ServerSocket通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 設置通道為 非阻塞
        serverChannel.configureBlocking(false);
        // 將該通道對于的serverSocket綁定到port端口
        serverChannel.socket().bind(new InetSocketAddress(port));
        // 獲得一耳光通道管理器
        this.selector = Selector.open();

        // 將通道管理器和該通道綁定,并為該通道注冊selectionKey.OP_ACCEPT事件
        // 注冊該事件后,當事件到達的時候,selector.select()會返回,
        // 如果事件沒有到達selector.select()會一直阻塞

        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    // 采用輪訓的方式監聽selector上是否有需要處理的事件,如果有,進行處理
    public void listen() throws Exception {
        System.out.println("start server");
        // 輪詢訪問selector
        while (true) {
            // 當注冊事件到達時,方法返回,否則該方法會一直阻塞
            selector.select();
            // 獲得selector中選中的相的迭代器,選中的相為注冊的事件
            Iterator ite = this.selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();
                // 刪除已選的key 以防重負處理
                ite.remove();
                // 客戶端請求連接事件
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    // 獲得和客戶端連接的通道
                    SocketChannel channel = server.accept();
                    // 設置成非阻塞
                    channel.configureBlocking(false);
                    // 在這里可以發送消息給客戶端
                    channel.write(ByteBuffer.wrap(new String("hello client").getBytes()));
                    // 在客戶端 連接成功之后,為了可以接收到客戶端的信息,需要給通道設置讀的權限
                    channel.register(this.selector, SelectionKey.OP_READ);
                    // 獲得了可讀的事件

                } else if (key.isReadable()) {
                    read(key);
                }

            }
        }
    }

    // 處理 讀取客戶端發來的信息事件
    private void read(SelectionKey key) throws Exception {
        // 服務器可讀消息,得到事件發生的socket通道
        SocketChannel channel = (SocketChannel) key.channel();
        // 穿件讀取的緩沖區
        ByteBuffer buffer = ByteBuffer.allocate(10);
        channel.read(buffer);
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("server receive from client: " + msg);
        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
        channel.write(outBuffer);
    }

    public static void main(String[] args) throws Throwable {
        NIOServer server = new NIOServer();
        server.initServer(8989);
        server.listen();
    }
}

客戶端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOClient {

    // 通道管理器
    private Selector selector;

    /**
     * * // 獲得一個Socket通道,并對該通道做一些初始化的工作 * @param ip 連接的服務器的ip // * @param port
     * 連接的服務器的端口號 * @throws IOException
     */
    public void initClient(String ip, int port) throws IOException { // 獲得一個Socket通道
        SocketChannel channel = SocketChannel.open(); // 設置通道為非阻塞
        channel.configureBlocking(false); // 獲得一個通道管理器
        this.selector = Selector.open(); // 客戶端連接服務器,其實方法執行并沒有實現連接,需要在listen()方法中調
        // 用channel.finishConnect();才能完成連接
        channel.connect(new InetSocketAddress(ip, port));
        // 將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_CONNECT事件。
        channel.register(selector, SelectionKey.OP_CONNECT);
    }

    /**
     * * // 采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理 * @throws // IOException
     * @throws Exception 
     */
    @SuppressWarnings("unchecked")
    public void listen() throws Exception { // 輪詢訪問selector
        while (true) {
            // 選擇一組可以進行I/O操作的事件,放在selector中,客戶端的該方法不會阻塞,
            // 這里和服務端的方法不一樣,查看api注釋可以知道,當至少一個通道被選中時,
            // selector的wakeup方法被調用,方法返回,而對于客戶端來說,通道一直是被選中的
            selector.select(); // 獲得selector中選中的項的迭代器
            Iterator ite = this.selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next(); // 刪除已選的key,以防重復處理
                ite.remove(); // 連接事件發生
                if (key.isConnectable()) {
                    SocketChannel channel = (SocketChannel) key.channel(); // 如果正在連接,則完成連接
                    if (channel.isConnectionPending()) {
                        channel.finishConnect();
                    } // 設置成非阻塞
                    channel.configureBlocking(false);
                    // 在這里可以給服務端發送信息哦
                    channel.write(ByteBuffer.wrap(new String("hello server!").getBytes()));
                    // 在和服務端連接成功之后,為了可以接收到服務端的信息,需要給通道設置讀的權限。
                    channel.register(this.selector, SelectionKey.OP_READ); // 獲得了可讀的事件
                } else if (key.isReadable()) {
                    read(key);
                }
            }
        }
    }

    private void read(SelectionKey key) throws Exception {
        SocketChannel channel = (SocketChannel) key.channel();
        // 穿件讀取的緩沖區
        ByteBuffer buffer = ByteBuffer.allocate(10);
        channel.read(buffer);
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("client receive msg from server:" + msg);
        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
        channel.write(outBuffer);

    }

    /**
     * * // 啟動客戶端測試 * @throws IOException
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        NIOClient client = new NIOClient();
        client.initClient("localhost", 8989);
        client.listen();
    }
}
參考資料

Java NIO系列教程

Java NIO學習8(Selector)

更多內容可以關注微信公眾號,或者訪問AppZone網站

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64819.html

相關文章

  • JAVA_NIO系列——Channel和Buffer詳解

    摘要:是一個用來替代標準的新型數據傳遞方式,像現在分布式架構中會經常存在他的身影。這個方法會一直阻塞到某個注冊的通道有事件就緒。保持不變,仍然表示能從中讀取多少個元素等與通過調用方法,可以標記中的一個特定。 Java NIO是一個用來替代標準Java IO API的新型數據傳遞方式,像現在分布式架構中會經常存在他的身影。其比傳統的IO更加高效,非阻塞,異步,雙向 NIO主體結構 showIm...

    leon 評論0 收藏0
  • TiKV 源碼解析系列文章(一)序

    摘要:而源碼解析系列文章則是會從源碼層面給大家抽絲剝繭,讓大家知道我們內部到底是如何實現的。我們希望通過該源碼解析系列,能讓大家對有一個更深刻的理解。 作者:唐劉 TiKV 是一個支持事務的分布式 Key-Value 數據庫,有很多社區開發者基于 TiKV 來開發自己的應用,譬如 titan、tidis。尤其是在 TiKV 成為 CNCF 的 Sandbox 項目之后,吸引了越來越多開發者的...

    LeviDing 評論0 收藏0
  • 【云解析 UDNS】操作指南:添加記錄

    摘要:添加記錄操作步驟進入域名解析頁面。,點擊添加記錄。,填寫詳細的記錄信息。配置說明配置說明主機記錄設置解析記錄的主機記錄名稱。標準應答為返回設置的全部記錄值,支持全部記錄類型隨機應答為根據權重隨機返回記錄值,僅支持記錄類型。添加記錄操作步驟1、進入域名解析 UDNS頁面。2,點擊添加記錄。3,填寫詳細的記錄信息。詳細配置說明見下方。4,點擊確定即可添加成功。配置說明配置說明主機記錄設置解析記錄...

    Tecode 評論0 收藏0
  • 【項目上線】詳細步驟04:在一臺云主機上部署多個網站,通過自定義網站名訪問項目地址

    摘要:安裝完成后登陸,注意,如果裝的是,協議改為。舉例我通過阿里云注冊的域名是那么這里我可以輸入,或者其他任何指定特定網站,一臺主機可以部署多個網站。 推薦安裝Xftp,是一個可視化管理云主機上文件的軟件,方便初學者學習。 安裝完成后登陸,showImg(https://segmentfault.com/img/bVZEAI?w=496&h=702); 注意,如果裝的是xftp 5,協議改為...

    Jrain 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<