NIO和IO多路復(fù)用

1.Buffer

常用ByteBuffer

  1. 其中的三個(gè)屬性position,limit,capacity
  • capacity 指的是容量,如capacity為1024的IntBuffer可以存放1024個(gè)int類型的值
  • position默認(rèn)在位置0,代表下一個(gè) 要讀或?qū)懙奈恢?每次讀寫一個(gè)數(shù)據(jù),后移一位
  • limit 寫模式下,代表最大寫入多少個(gè)數(shù)據(jù),此時(shí)和capacity相同,讀模式下,代表最多讀多少個(gè)數(shù)據(jù)(數(shù)據(jù)個(gè)數(shù))
  1. 一些常用方法
ByteBuffer byteBuf = ByteBuffer.allocate(1024);
public static ByteBuffer wrap(byte[] array) {
    ...
}
  1. 一些api
    寫模式切換到讀模式的flip
public final Buffer flip() {
    limit = position; // 將 limit 設(shè)置為實(shí)際寫入的數(shù)據(jù)數(shù)量
    position = 0; // 重置 position 為 0
    mark = -1; // mark 之后再說
    return this;
}

讀完之后可能重新用buffer來寫,使用clear重新設(shè)置

public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

當(dāng)讀到第5個(gè)數(shù)據(jù)時(shí)先mark一下,讀到10時(shí)reset一下就可以實(shí)現(xiàn)重新從第5個(gè)數(shù)據(jù)讀mark reset.就是將position的位置記錄一下,等會(huì)再修改回來.

public final Buffer mark() {
    mark = position;
    return this;
}
public final Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}

2.channel

  1. 和buffer的讀寫
    將數(shù)據(jù)從buffer寫到channel里面channel.write(buffer)
  2. channel的實(shí)現(xiàn)類有FileChannel SocketChannel ServerSocketChannel
  3. 使用channel實(shí)現(xiàn)文件拷貝
static void channel() throws IOException {
        File file = new File("F:\\in.txt");
        File file1 = new File("F:\\out.txt");
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        FileInputStream fileInputStream = new FileInputStream(file);
        FileOutputStream outputStream = new FileOutputStream(file1);
        FileChannel inChannel = fileInputStream.getChannel();
        FileChannel outChannel = outputStream.getChannel();
        int len = -1;
        while ((len = inChannel.read(byteBuffer)) != -1) {
            byteBuffer.flip();
            outChannel.write(byteBuffer);
            byteBuffer.clear();

        }
        fileInputStream.close();
        outputStream.close();
    }

SocketChannel和ServerSocketChannel

// 打開一個(gè)通道
SocketChannel socketChannel = SocketChannel.open();
// 發(fā)起連接
socketChannel.connect(new InetSocketAddress("https://www.javadoop.com", 80));

// 實(shí)例化
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 監(jiān)聽 8080 端口
serverSocketChannel.socket().bind(new InetSocketAddress(8080));

while (true) {
    // 一旦有一個(gè) TCP 連接進(jìn)來,就對(duì)應(yīng)創(chuàng)建一個(gè) SocketChannel 進(jìn)行處理
    SocketChannel socketChannel = serverSocketChannel.accept();
}

3.Selector

可以監(jiān)聽多個(gè)Channel
可以監(jiān)聽channel的讀事件,寫事件,建立連接的事件
selectionKey = channel.register(selector,read)將通道注冊(cè)到selector上,并監(jiān)聽可讀的請(qǐng)求.
之后可以通過Set<SelectionKey> selectedKeys = selector.selectedKeys();獲取SelectionKey的集合,通過遍歷處理事件(如果是連接請(qǐng)求,創(chuàng)建一個(gè)新的SocketChannel并注冊(cè)到selector上,如果是可讀的事件,調(diào)用channel.read(buffer)接收數(shù)據(jù))
舉例

public class SelectorServer {

    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();

        ServerSocketChannel server = ServerSocketChannel.open();
        server.socket().bind(new InetSocketAddress(8080));

        // 將其注冊(cè)到 Selector 中,監(jiān)聽 OP_ACCEPT 事件
        server.configureBlocking(false);
        server.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            int readyChannels = selector.select();
            if (readyChannels == 0) {
                continue;
            }
            Set<SelectionKey> readyKeys = selector.selectedKeys();
            // 遍歷
            Iterator<SelectionKey> iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    // 有已經(jīng)接受的新的到服務(wù)端的連接
                    SocketChannel socketChannel = server.accept();

                    // 有新的連接并不代表這個(gè)通道就有數(shù)據(jù)坞笙,
                    // 這里將這個(gè)新的 SocketChannel 注冊(cè)到 Selector鞋邑,監(jiān)聽 OP_READ 事件,等待數(shù)據(jù)
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    // 有數(shù)據(jù)可讀
                    // 上面一個(gè) if 分支中注冊(cè)了監(jiān)聽 OP_READ 事件的 SocketChannel
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                    int num = socketChannel.read(readBuffer);
                    if (num > 0) {
                        // 處理進(jìn)來的數(shù)據(jù)...
                        System.out.println("收到數(shù)據(jù):" + new String(readBuffer.array()).trim());
                        ByteBuffer buffer = ByteBuffer.wrap("返回給客戶端的數(shù)據(jù)...".getBytes());
                        socketChannel.write(buffer);
                    } else if (num == -1) {
                        // -1 代表連接已經(jīng)關(guān)閉
                        socketChannel.close();
                    }
                }
            }
        }
    }
}

4.NIO的Selector和linux的select poll epoll

Selector封裝了后面三種方式,對(duì)于linux來講,可能用的其中一個(gè)

5.select

一個(gè)進(jìn)程管理多個(gè)socket的連接

  • 進(jìn)程A調(diào)用select進(jìn)入阻塞,即進(jìn)入每個(gè)socket的等待隊(duì)列.由于CPU并行執(zhí)行其他進(jìn)程,因此進(jìn)入阻塞的進(jìn)程不會(huì)占用CPU
  • select調(diào)用時(shí)傳入需要管理的socket集合,將對(duì)應(yīng)的文件描述符由用戶進(jìn)程傳入內(nèi)核,并進(jìn)行檢查有沒有對(duì)應(yīng)的事件到達(dá),如果有則立即返回.
  • 當(dāng)網(wǎng)絡(luò)數(shù)據(jù)到達(dá)時(shí),引發(fā)中斷,cpu調(diào)用中斷處理程序,將數(shù)據(jù)通過DMA 硬件等方式由網(wǎng)卡傳遞到socket的讀緩沖區(qū)
  • 進(jìn)程A由于等待的事件到達(dá)被喚醒,從socket的等待隊(duì)列移動(dòng)到進(jìn)程運(yùn)行的隊(duì)列
  • 即select函數(shù)返回值大于0,表示有多少個(gè)等待的事件到達(dá),這時(shí)需要遍歷所有socket,看看是哪個(gè)socket的事件發(fā)生了
  • 處理事件
    epoll
    epoll_create創(chuàng)建對(duì)象
    epoll_ctl將管理的socket加入到epoll對(duì)象當(dāng)中
    epoll_wait等待數(shù)據(jù)到達(dá)
  • 首先調(diào)用epoll_create創(chuàng)建eventpoll對(duì)象
  • 調(diào)用epoll_ctl會(huì)將eventpoll對(duì)象加入到需要管理的socket的等待隊(duì)列當(dāng)中
  • 當(dāng)數(shù)據(jù)到達(dá)網(wǎng)卡,觸發(fā)中斷信號(hào),中斷程序?qū)?shù)據(jù)由網(wǎng)卡通過DMA 硬件等方式拷貝到內(nèi)存,還會(huì)操作socket的等待隊(duì)列中的eventpoll對(duì)象,其維護(hù)了一個(gè)就緒列表,存放有事件發(fā)送的socket
  • 調(diào)用epol_wait時(shí),如果eventpoll對(duì)象里面的就緒列表中已經(jīng)有引用的socket,就會(huì)返回,如果沒有會(huì)阻塞進(jìn)程.
  • eventpoll對(duì)象維護(hù)監(jiān)視的socket集合是通過紅黑樹來維護(hù),而就緒列表是通過雙向鏈表來維護(hù).
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秒裕,一起剝皮案震驚了整個(gè)濱河市袱蚓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌几蜻,老刑警劉巖喇潘,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異梭稚,居然都是意外死亡颖低,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門弧烤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忱屑,“玉大人,你說我怎么就攤上這事暇昂≥航洌” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵急波,是天一觀的道長(zhǎng)从铲。 經(jīng)常有香客問我,道長(zhǎng)澄暮,這世上最難降的妖魔是什么名段? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任阱扬,我火速辦了婚禮,結(jié)果婚禮上伸辟,老公的妹妹穿的比我還像新娘麻惶。我一直安慰自己,他們只是感情好自娩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布用踩。 她就那樣靜靜地躺著,像睡著了一般忙迁。 火紅的嫁衣襯著肌膚如雪脐彩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天姊扔,我揣著相機(jī)與錄音惠奸,去河邊找鬼。 笑死恰梢,一個(gè)胖子當(dāng)著我的面吹牛佛南,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嵌言,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼嗅回,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了摧茴?” 一聲冷哼從身側(cè)響起绵载,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苛白,沒想到半個(gè)月后娃豹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡购裙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年懂版,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躏率。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躯畴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出禾锤,到底是詐尸還是另有隱情私股,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布恩掷,位于F島的核電站倡鲸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏黄娘。R本人自食惡果不足惜峭状,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一克滴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧优床,春花似錦劝赔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至移层,卻和暖如春仍翰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背观话。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工予借, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人频蛔。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓灵迫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親晦溪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瀑粥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • java NIO 開始于JDK1.4,其核心元件有:Channel三圆、Buffer利凑、Selector。 Channe...
    join_a922閱讀 634評(píng)論 0 0
  • # Java NIO # Java NIO屬于非阻塞IO嫌术,這是與傳統(tǒng)IO最本質(zhì)的區(qū)別。傳統(tǒng)IO包括socket和文...
    Teddy_b閱讀 583評(píng)論 0 0
  • 前言 現(xiàn)在使用NIO的場(chǎng)景越來越多牌借,很多網(wǎng)上的技術(shù)框架或多或少的使用NIO技術(shù)度气,譬如Tomcat,Jetty膨报。學(xué)習(xí)...
    路遠(yuǎn)處幽閱讀 667評(píng)論 1 0
  • ------NIO簡(jiǎn)介(1)-------- NIO組件 channel磷籍,buffer,selector现柠,pip院领,...
    任嘉平生愿閱讀 538評(píng)論 1 0
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個(gè)新的IO API,可以替代標(biāo)準(zhǔn)的Java I...
    zhisheng_blog閱讀 1,115評(píng)論 0 7