網絡編程---NIO基本案例

歡迎大家訪問我的個人博客:L_SKH’Blog

一、首先要知道NIO的三大組件以及他們之間的關係:

SelectorChannelBuffer 的關係圖
在這裏插入圖片描述
關係圖的說明:
1.每個channel 都會對應一個Buffer
2.Selector 對應一個線程, 一個線程對應多個channel(連接)
3.該圖反應了有三個channel 註冊到 該selector //程序
程序切換到哪個channel 是由事件決定的, Event 就是一個重要的概念
Selector 會根據不同的事件,在各個通道上切換
4.Buffer 就是一個內存塊 , 底層是有一個數組
數據的讀取寫入是通過Buffer, 這個和BIO , BIO 中要麼是輸入流,或者是輸出流, 不能雙向,但是NIO的Buffer 是可以讀也可以寫, 需要 flip 方法切換
channel 是雙向的, 可以返回底層操作系統的情況, 比如Linux , 底層的操作系統通道就是雙向的.

二、基本流程

在這裏插入圖片描述
說明:
1.當客戶端連接時,會通過ServerSocketChannel 得到 SocketChannel
2.Selector 進行監聽 select 方法, 返回有事件發生的通道的個數.
3.將socketChannel註冊到Selector上, register(Selector sel, int ops), 一個selector上可以註冊多個SocketChannel
4.註冊後返回一個 SelectionKey, 會和該Selector 關聯(集合)
5.進一步得到各個 SelectionKey (有事件發生)。在通過 SelectionKey 反向獲取 SocketChannel , 方法 channel()
6.最後可以通過 得到的 channel , 完成業務處理

三、代碼實現

3.1NIOServer

package org.skh.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
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;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/26 17:19
 */

/**
 * 要注意這個buffer 客戶端和channel之間存在
 * 服務器與通道之間也存在
 */
public class T07_NIOServer {
    public static void main(String[] args) throws IOException {

        //1.創建ServerSocketChannel 相當於ServerSocket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //2.開啓一個selector對象
        Selector selector = Selector.open();

        //3.綁定端口 在服務器監聽
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

        //4.設置爲非阻塞
        serverSocketChannel.configureBlocking(false);

        //5.把serverChannel註冊到Selector 關注accept事件 客戶端連接
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //6.循環等待客戶端連接
        while (true) {

            //讓selector阻塞式的監聽一秒看是否有事件發生
            if (selector.select(1000) == 0) {
                System.out.println("逝去的一秒內無客戶端連接....");
                continue;
            }

            //!=0說明有事件發生 所以我們要取得selectionKey集合
            //得到關注事件的集合 進而得到對應的channel
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            //使用迭代器遍歷
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            while (keyIterator.hasNext()) { //更具相應的key的事件來進行處理
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    //連接事件 生成socketchannel 並註冊到selector
                    //設置其事件爲只讀 並關聯一個Buffer
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //將socketchannel設置爲非阻塞模型
                    socketChannel.configureBlocking(false)  ;

                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                if (key.isReadable()) {

                    //通過key反向獲取對應的channel
                    SocketChannel channel = (SocketChannel) key.channel();
                    //得到關聯的buffer
                    ByteBuffer buffer = (ByteBuffer) key.attachment();

                    channel.read(buffer); //服務器與客戶端之間也有buffer 所以要讀取channel到buffer
                    System.out.println("From 客戶端: " + new String(buffer.array(),0,buffer.position()));
                }
                //處理完之後就消除 避免重複處理
                keyIterator.remove();
            }


        }
    }
}

3.2NIOClient

package org.skh.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * @Created IntelliJ IDEA.
 * @Author L_SKH
 * @Date 2019/11/26 18:49
 */
public class T08_NIOClient {
    public static void main(String[] args) throws IOException {
        //得到一個channel
        SocketChannel socketChannel = SocketChannel.open();
        //設置非阻塞
        socketChannel.configureBlocking(false) ;
        InetSocketAddress address = new InetSocketAddress("localhost", 6666);
        if (!socketChannel.connect(address)){
            //建立連接的過程需要時間 但因爲是非阻塞的所以不會停留等待
            //可以去做其他事情 所以我們寫一個while循環 以避免還沒有連接就去發送數據
            //同時也可以很好的看到非阻塞的效果
            while (!socketChannel.finishConnect()){
                System.out.println("Doing Others While Connecting!!!");
            }
        }

        //已建立好連接 向客戶端發送數據
        String msg =  "Rush_SKH" ;
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        socketChannel.write(buffer)  ;
        System.in.read()  ;

    }
}

3.3Console:

逝去的一秒內無客戶端連接....
逝去的一秒內無客戶端連接....
From 客戶端: Rush_SKH
逝去的一秒內無客戶端連接....
逝去的一秒內無客戶端連接....
逝去的一秒內無客戶端連接....
逝去的一秒內無客戶端連接....
發佈了147 篇原創文章 · 獲贊 62 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章