NIO(一)

通過Netty權(quán)威指南上的一個簡單NIO時間服務(wù)器

TimeServer

import org.apache.commons.lang.StringUtils;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

/**
 * Created by liqiushi on 2017/12/8.
 */
public class TimeServer implements Runnable {

    private ServerSocketChannel serverSocketChannel;
    private Selector selector;
    private volatile boolean stop;

    public TimeServer(int port) {

        try {
            //1幢码、打開ServerSocketChannel
            serverSocketChannel = ServerSocketChannel.open();
            //2睛廊、綁定監(jiān)聽端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            serverSocketChannel.configureBlocking(false);

            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("The time server start! port:" + port);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //3腺劣、啟用多路復(fù)用

    }

    @Override
    public void run() {
        while (!stop) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                SelectionKey selectionKey = null;
                while (iterator.hasNext()) {
                    selectionKey = iterator.next();
                    iterator.remove();
                    try {
                        handleInPut(selectionKey);
                    } catch (Exception e) {
                        if (selectionKey != null) {
                            selectionKey.cancel();
                            if (selectionKey.channel() != null) {
                                selectionKey.channel().close();
                            }
                        }
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (selector != null) {
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void stop() {
        this.stop = true;
    }

    private void handleInPut(SelectionKey key) throws IOException {
        if (key.isValid()) {
            if (key.isAcceptable()) {
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
                SocketChannel socketChannel = (SocketChannel) key.channel();
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);

                int readBytes = socketChannel.read(readBuffer);
                if (readBytes > 0) {
                    //轉(zhuǎn)換成寫模式
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("The time server recieve order : " + body);
                    String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ?
                            new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
                    //write
                    dowrite(socketChannel, currentTime);
                } else if (readBytes < 0) {
                    key.cancel();
                    socketChannel.close();
                } else {
                    //0字節(jié)
                }
            }
        }
    }

    private void dowrite(SocketChannel socketChannel, String response) throws IOException {
        if (!StringUtils.isBlank(response)) {
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            socketChannel.write(writeBuffer);
        }
    }

    public static void main(String[] args) {
        TimeServer timeServer = new TimeServer(8001);
        new Thread(timeServer, "NIO-MultiplexerTimeServer-001").start();
    }
}

幾個疑問

selector

有幾個keySet:1柴底、注冊(publicKeys) 2倍试、準(zhǔn)備就緒(selectedKeys)3逗爹、調(diào)用了cancel()后谷市,將channel對應(yīng)的key加入cancelledKeys集中

cancel()

調(diào)用了SelectionKey對象的cancel方法蛔垢,這個SelectionKey對象就會被加入到cancelled-keys集合中,表示這個SelectionKey對象已經(jīng)被取消歌懒。

在每次選擇操作期間啦桌,都可以將鍵添加到選擇器的已選擇鍵集以及從中將其移除,并且可以從其鍵集和已取消鍵集中將其移除及皂。選擇是由 select()甫男、select(long) 和 selectNow() 方法執(zhí)行的,執(zhí)行涉及三個步驟:

1.將已取消鍵集中的每個鍵從所有鍵集中移除(如果該鍵是鍵集的成員)验烧,并注銷其通道板驳。此步驟使已取消鍵集成為空集。
2.在開始進行選擇操作時碍拆,應(yīng)查詢基礎(chǔ)操作系統(tǒng)來更新每個剩余通道的準(zhǔn)備就緒信息若治,以執(zhí)行由其鍵的相關(guān)集合所標(biāo)識的任意操作。對于已為至少一個這樣的操作準(zhǔn)備就緒的通道感混,執(zhí)行以下兩種操作之一:

  • a.如果該通道的鍵尚未在已選擇鍵集中端幼,則將其添加到該集合中,并修改其準(zhǔn)備就緒操作集弧满,以準(zhǔn)確地標(biāo)識那些通道現(xiàn)在已報告為之準(zhǔn)備就緒的操作婆跑。丟棄準(zhǔn)備就緒操作集中以前記錄的所有準(zhǔn)備就緒信息。
  • b. 如果該通道的鍵已經(jīng)在已選擇鍵集中庭呜,則修改其準(zhǔn)備就緒操作集滑进,以準(zhǔn)確地標(biāo)識所有通道已報告為之準(zhǔn)備就緒的新操作。保留準(zhǔn)備就緒操作集以前記錄的所有準(zhǔn)備就緒信息募谎;換句話說扶关,基礎(chǔ)系統(tǒng)所返回的準(zhǔn)備就緒操作集是和該鍵的當(dāng)前準(zhǔn)備就緒操作集按位分開 (bitwise-disjoined) 的。

3.如果在此步驟開始時鍵集中的所有鍵都有空的相關(guān)集合数冬,則不會更新已選擇鍵集和任意鍵的準(zhǔn)備就緒操作集节槐。
如果在步驟2的執(zhí)行過程中要將任意鍵添加到已取消鍵集中,則處理過程如步驟1。

JAVA NIO 不是同步非阻塞I/O嗎铜异,為什么說JAVA NIO提供了基于Selector的異步網(wǎng)絡(luò)I/O地来?

I/O操作 在前面的文章提到,分為兩步:

  1. 詢問內(nèi)核空間是否有接受到數(shù)據(jù)的到來
  2. 進行read/write讀寫操作
  • JAVA NIO是基于select 多路復(fù)用模型熙掺,linux下(epoll)未斑,那么結(jié)論就是同步非阻塞,阻塞與非阻塞在于是否需要用戶阻塞于詢問數(shù)據(jù)的過程币绩,而同步異步在于I/O的具體操作(內(nèi)核空間到 用戶緩沖區(qū)的copy)蜡秽,數(shù)據(jù)到來,NIO依舊是一個同步操作的過程缆镣,異步則是I/O由內(nèi)核完成芽突,當(dāng)完成時再來提醒用戶進行其他的操作。
    下面回答第二個問題:為什么說是異步網(wǎng)絡(luò)I/O(引用他人之言)

而說java nio提供了異步處理董瞻,這個異步應(yīng)該是指編程模型上的異步寞蚌。基于reactor模式的事件驅(qū)動钠糊,事件處理器的注冊和處理器的執(zhí)行是異步的挟秤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抄伍,隨后出現(xiàn)的幾起案子艘刚,更是在濱河造成了極大的恐慌,老刑警劉巖截珍,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攀甚,死亡現(xiàn)場離奇詭異,居然都是意外死亡岗喉,警方通過查閱死者的電腦和手機秋度,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钱床,“玉大人荚斯,你說我怎么就攤上這事〉觯” “怎么了鲸拥?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵拐格,是天一觀的道長僧免。 經(jīng)常有香客問我,道長捏浊,這世上最難降的妖魔是什么懂衩? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上浊洞,老公的妹妹穿的比我還像新娘牵敷。我一直安慰自己,他們只是感情好法希,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布枷餐。 她就那樣靜靜地躺著,像睡著了一般苫亦。 火紅的嫁衣襯著肌膚如雪毛肋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天屋剑,我揣著相機與錄音润匙,去河邊找鬼。 笑死唉匾,一個胖子當(dāng)著我的面吹牛孕讳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巍膘,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼厂财,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了峡懈?” 一聲冷哼從身側(cè)響起蟀苛,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逮诲,沒想到半個月后帜平,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡梅鹦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年裆甩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齐唆。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗤栓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箍邮,到底是詐尸還是另有隱情茉帅,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布锭弊,位于F島的核電站堪澎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏味滞。R本人自食惡果不足惜樱蛤,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一钮呀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昨凡,春花似錦爽醋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哪痰,卻和暖如春证杭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妒御。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工解愤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乎莉。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓送讲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惋啃。 傳聞我的和親對象是個殘疾皇子哼鬓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

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