Java NIO

Java NIO 由以下幾個核心部分組成:

Channels
Buffers
Selectors

雖然Java NIO 中除此之外還有很多類和組件,但在我看來篙程,Channel胁后,Buffer 和 Selector 構(gòu)成了核心的API伐债。其它組件趣兄,如Pipe和FileLock驻民,只不過是與三個核心組件共同使用的工具類翻具。因此,在概述中我將集中在這三個組件上回还。其它組件會在單獨的章節(jié)中講到裆泳。

Channel 和 Buffer
基本上,所有的 IO 在NIO 中都從一個Channel 開始柠硕。Channel 有點象流工禾。 數(shù)據(jù)可以從Channel讀到Buffer中,也可以從Buffer 寫到Channel中蝗柔。這里有個圖示:


overview-channels-buffers1.png

Channel和Buffer有好幾種類型闻葵。下面是JAVA NIO中的一些主要Channel的實現(xiàn):

FileChannel ---------文件
DatagramChannel ------------UDP
SocketChannel ---------------TCP
ServerSocketChannel -------------TCP

正如你所看到的,這些通道涵蓋了UDP 和 TCP 網(wǎng)絡IO癣丧,以及文件IO槽畔。

與這些類一起的有一些有趣的接口瘫怜,但為簡單起見仇穗,我盡量在概述中不提到它們妇智。本教程其它章節(jié)與它們相關的地方我會進行解釋硼身。

以下是Java NIO里關鍵的Buffer實現(xiàn):

ByteBuffer
ShortBuffer
IntBuffer
LongBuffer

FloatBuffer
DoubleBuffer

CharBuffer

這些Buffer覆蓋了你能通過IO發(fā)送的基本數(shù)據(jù)類型:byte, short, int, long, float, double 和 char垃它。

Java NIO 還有個 MappedByteBuffer筷黔,用于表示內(nèi)存映射文件草巡, 我也不打算在概述中說明唯卖。

Selector
Selector允許單線程處理多個 Channel憎夷。如果你的應用打開了多個連接(通道)莽鸿,但每個連接的流量都很低,使用Selector就會很方便。例如祥得,在一個聊天服務器中兔沃。

這是在一個單線程中使用一個Selector處理3個Channel的圖示:


overview-selectors.png

要使用Selector,得向Selector注冊Channel级及,然后調(diào)用它的select()方法乒疏。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法返回饮焦,線程就可以處理這些事件怕吴,事件的例子有如新連接進來,數(shù)據(jù)接收等县踢。

代碼示例:

import java.io.IOException;  
import java.net.InetSocketAddress;  
import java.net.ServerSocket;  
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;  
import java.util.Set;  
 
public class NIOServer {  
      
    /*標識數(shù)字*/ 
    private  int flag = 0;  
    /*緩沖區(qū)大小*/ 
    private  int BLOCK = 4096;  
    /*接受數(shù)據(jù)緩沖區(qū)*/ 
    private  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
    /*發(fā)送數(shù)據(jù)緩沖區(qū)*/ 
    private  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
    private  Selector selector;  
 
    public NIOServer(int port) throws IOException {  
        // 打開服務器套接字通道  
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
        // 服務器配置為非阻塞  
        serverSocketChannel.configureBlocking(false);  
        // 檢索與此通道關聯(lián)的服務器套接字  
        ServerSocket serverSocket = serverSocketChannel.socket();  
        // 進行服務的綁定  
        serverSocket.bind(new InetSocketAddress(port));  
        // 通過open()方法找到Selector  
        selector = Selector.open();  
        // 注冊到selector转绷,等待連接  
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
        System.out.println("Server Start----8888:");  
    }  
 
 
    // 監(jiān)聽  
    private void listen() throws IOException {  
        while (true) {  
            // 選擇一組鍵,并且相應的通道已經(jīng)打開  
            selector.select();  
            // 返回此選擇器的已選擇鍵集硼啤。  
            Set<SelectionKey> selectionKeys = selector.selectedKeys();  
            Iterator<SelectionKey> iterator = selectionKeys.iterator();  
            while (iterator.hasNext()) {          
                SelectionKey selectionKey = iterator.next();  
                iterator.remove();  
                handleKey(selectionKey);  
            }  
        }  
    }  
 
    // 處理請求  
    private void handleKey(SelectionKey selectionKey) throws IOException {  
        // 接受請求  
        ServerSocketChannel server = null;  
        SocketChannel client = null;  
        String receiveText;  
        String sendText;  
        int count=0;  
        // 測試此鍵的通道是否已準備好接受新的套接字連接议经。  
        if (selectionKey.isAcceptable()) {  
            // 返回為之創(chuàng)建此鍵的通道。  
            server = (ServerSocketChannel) selectionKey.channel();  
            // 接受到此通道套接字的連接谴返。  
            // 此方法返回的套接字通道(如果有)將處于阻塞模式煞肾。  
            client = server.accept();  
            // 配置為非阻塞  
            client.configureBlocking(false);  
            // 注冊到selector,等待連接  
            client.register(selector, SelectionKey.OP_READ);  
        } else if (selectionKey.isReadable()) {  
            // 返回為之創(chuàng)建此鍵的通道嗓袱。  
            client = (SocketChannel) selectionKey.channel();  
            //將緩沖區(qū)清空以備下次讀取  
            receivebuffer.clear();  
            //讀取服務器發(fā)送來的數(shù)據(jù)到緩沖區(qū)中  
            count = client.read(receivebuffer);   
            if (count > 0) {  
                receiveText = new String( receivebuffer.array(),0,count);  
                System.out.println("服務器端接受客戶端數(shù)據(jù)--:"+receiveText);  
                client.register(selector, SelectionKey.OP_WRITE);  
            }  
        } else if (selectionKey.isWritable()) {  
            //將緩沖區(qū)清空以備下次寫入  
            sendbuffer.clear();  
            // 返回為之創(chuàng)建此鍵的通道籍救。  
            client = (SocketChannel) selectionKey.channel();  
            sendText="message from server--" + flag++;  
            //向緩沖區(qū)中輸入數(shù)據(jù)  
            sendbuffer.put(sendText.getBytes());  
             //將緩沖區(qū)各標志復位,因為向里面put了數(shù)據(jù)標志被改變要想從中讀取數(shù)據(jù)發(fā)向服務器,就要復位  
            sendbuffer.flip();  
            //輸出到通道  
            client.write(sendbuffer);  
            System.out.println("服務器端向客戶端發(fā)送數(shù)據(jù)--:"+sendText);  
            client.register(selector, SelectionKey.OP_READ);  
        }  
    }  
 
    /**  
     * @param args  
     * @throws IOException  
     */ 
    public static void main(String[] args) throws IOException {  
        // TODO Auto-generated method stub  
        int port = 8888;  
        NIOServer server = new NIOServer(port);  
        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;  
import java.util.Set;  
 
public class NIOClient {  
 
    /*標識數(shù)字*/ 
    private static int flag = 0;  
    /*緩沖區(qū)大小*/ 
    private static int BLOCK = 4096;  
    /*接受數(shù)據(jù)緩沖區(qū)*/ 
    private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
    /*發(fā)送數(shù)據(jù)緩沖區(qū)*/ 
    private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
    /*服務器端地址*/ 
    private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(  
            "localhost", 8888);  
 
    public static void main(String[] args) throws IOException {  
        // TODO Auto-generated method stub  
        // 打開socket通道  
        SocketChannel socketChannel = SocketChannel.open();  
        // 設置為非阻塞方式  
        socketChannel.configureBlocking(false);  
        // 打開選擇器  
        Selector selector = Selector.open();  
        // 注冊連接服務端socket動作  
        socketChannel.register(selector, SelectionKey.OP_CONNECT);  
        // 連接  
        socketChannel.connect(SERVER_ADDRESS);  
        // 分配緩沖區(qū)大小內(nèi)存  
          
        Set<SelectionKey> selectionKeys;  
        Iterator<SelectionKey> iterator;  
        SelectionKey selectionKey;  
        SocketChannel client;  
        String receiveText;  
        String sendText;  
        int count=0;  
 
        while (true) {  
            //選擇一組鍵,其相應的通道已為 I/O 操作準備就緒索抓。  
            //此方法執(zhí)行處于阻塞模式的選擇操作钧忽。  
            selector.select();  
            //返回此選擇器的已選擇鍵集。  
            selectionKeys = selector.selectedKeys();  
            //System.out.println(selectionKeys.size());  
            iterator = selectionKeys.iterator();  
            while (iterator.hasNext()) {  
                selectionKey = iterator.next();  
                if (selectionKey.isConnectable()) {  
                    System.out.println("client connect");  
                    client = (SocketChannel) selectionKey.channel();  
                    // 判斷此通道上是否正在進行連接操作逼肯。  
                    // 完成套接字通道的連接過程。  
                    if (client.isConnectionPending()) {  
                        client.finishConnect();  
                        System.out.println("完成連接!");  
                        sendbuffer.clear();  
                        sendbuffer.put("Hello,Server".getBytes());  
                        sendbuffer.flip();  
                        client.write(sendbuffer);  
                    }  
                    client.register(selector, SelectionKey.OP_READ);  
                } else if (selectionKey.isReadable()) {  
                    client = (SocketChannel) selectionKey.channel();  
                    //將緩沖區(qū)清空以備下次讀取  
                    receivebuffer.clear();  
                    //讀取服務器發(fā)送來的數(shù)據(jù)到緩沖區(qū)中  
                    count=client.read(receivebuffer);  
                    if(count>0){  
                        receiveText = new String( receivebuffer.array(),0,count);  
                        System.out.println("客戶端接受服務器端數(shù)據(jù)--:"+receiveText);  
                        client.register(selector, SelectionKey.OP_WRITE);  
                    }  
 
                } else if (selectionKey.isWritable()) {  
                    sendbuffer.clear();  
                    client = (SocketChannel) selectionKey.channel();  
                    sendText = "message from client--" + (flag++);  
                    sendbuffer.put(sendText.getBytes());  
                     //將緩沖區(qū)各標志復位,因為向里面put了數(shù)據(jù)標志被改變要想從中讀取數(shù)據(jù)發(fā)向服務器,就要復位  
                    sendbuffer.flip();  
                    client.write(sendbuffer);  
                    System.out.println("客戶端向服務器端發(fā)送數(shù)據(jù)--:"+sendText);  
                    client.register(selector, SelectionKey.OP_READ);  
                }  
            }  
            selectionKeys.clear();  
        }  
    }  
} 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桃煎,一起剝皮案震驚了整個濱河市篮幢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌为迈,老刑警劉巖三椿,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異葫辐,居然都是意外死亡搜锰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門耿战,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛋叼,“玉大人,你說我怎么就攤上這事”蜂蹋” “怎么了狐胎?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長歌馍。 經(jīng)常有香客問我握巢,道長,這世上最難降的妖魔是什么松却? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任暴浦,我火速辦了婚禮,結(jié)果婚禮上晓锻,老公的妹妹穿的比我還像新娘歌焦。我一直安慰自己,他們只是感情好带射,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布同规。 她就那樣靜靜地躺著,像睡著了一般窟社。 火紅的嫁衣襯著肌膚如雪券勺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天灿里,我揣著相機與錄音关炼,去河邊找鬼。 笑死匣吊,一個胖子當著我的面吹牛儒拂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播色鸳,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼社痛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了命雀?” 一聲冷哼從身側(cè)響起蒜哀,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吏砂,沒想到半個月后撵儿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡狐血,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年淀歇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匈织。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡浪默,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浴鸿,我是刑警寧澤井氢,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站岳链,受9級特大地震影響花竞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掸哑,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一约急、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苗分,春花似錦厌蔽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至择浊,卻和暖如春戴卜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背琢岩。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工投剥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人担孔。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓江锨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糕篇。 傳聞我的和親對象是個殘疾皇子啄育,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

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