1. 程式人生 > >Java NIO小案例

Java NIO小案例

package com.gsau.NIO;

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.Iterator;
/**
 * @Description:  建立NIOServer
 * @Date: 2018/9/12 13:16
 * @author: wgq
 * @version: 1.0
 * @param:
 */
public class NIOServer {
    private Selector selector;                                            // 通道管理器
    /**
    * @Description: 獲得一個通道並且做一些初始化的工作(為他註冊一個Selector)
    * @Date: 2018/9/12 13:17
    * @author: wgq
    * @version: 1.0
    * @param: 
    */
    public void initServer(int port) throws IOException {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();    // 獲得一個ServerSocket通道,相當於傳統socket中的ServerSocket
        serverChannel.configureBlocking(false);                            // 設定通道為非阻塞
        serverChannel.socket().bind(new InetSocketAddress(port));          // 將該通道對應的ServerSocket繫結到port埠,ip預設就是本機的ip
        this.selector = Selector.open();                                   // 獲得一個通道管理器
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);          // 將通道管理器和該通道繫結,併為該通道註冊SelectionKey.OP_ACCEPT事件,註冊該事件後,當該事件到達時selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞
    }
    /**
    * @Description:  採用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理
    * @Date: 2018/9/12 13:19
    * @author: wgq
    * @version: 1.0
    * @param: 
    */
    public void listen() throws IOException {
        System.out.println("服務端啟動成功!");
        while (true) {                                                               // 輪詢訪問selector
            selector.select();                                                       // 當註冊的事件到達時,方法返回;否則,該方法會一直阻塞,當一個事件到達的時候他會自動監聽到
            Iterator<?> ite = this.selector.selectedKeys().iterator();               // 獲得selector中選中的項的迭代器,選中的項為註冊的事件
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();                        //獲取選中的KEY
                ite.remove();                                                        // 刪除已選的key,以防重複處理
                handler(key);
            }
        }
    }
    /**
    * @Description: 處理請求的控制代碼
    * @Date: 2018/9/12 13:19
    * @author: wgq
    * @version: 1.0
    * @param: 
    */
    public void handler(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {                          // 客戶端請求連線事件
            handlerAccept(key);
        } else if (key.isReadable()) {                     // 獲得可讀的事件
            handelerRead(key);
        }
    }
    /**
    * @Description: 處理連線的請求
    * @Date: 2018/9/12 13:22
    * @author: wgq
    * @version: 1.0
    * @param: 
    */
    public void handlerAccept(SelectionKey key) throws IOException {
        ServerSocketChannel server = (ServerSocketChannel) key.channel();   //開啟一個通道和客戶端進行互動
        SocketChannel channel = server.accept();                            // 獲得和客戶端連線的通道
        channel.configureBlocking(false);                                   // 設定成非阻塞
        System.out.println("新的客戶端連線");                               // 在這裡可以給客戶端傳送資訊哦
        channel.register(this.selector, SelectionKey.OP_READ);               // 在和客戶端連線成功之後,為了可以接收到客戶端的資訊,需要給通道設定讀的許可權。
    }
     /**
     * @Description: 處理讀的事件
      *      ByteBuffer:ByteBuffer的讀寫模式是分開的
      *           建立方式:
      *               1.ByteBuffer buffer=ByteBuffer.allocate(256);
      *               2.ByteBuffer buffer=ByteBuffer.wrap(byteArray);這裡的byteArray可以包含了資料,相當於寫入了資料到緩衝區
      *            寫入資料:
      *               1.ByteBuffer buffer=ByteBuffer.wrap(byteArray)
      *               2.buffer.put(bytes)
      *            清除快取區:
      *               buffer.clear(); 這個方法實際上也不會改變緩衝區的資料,而只是簡單的重置了緩衝區的主要索引值,不必為了每次讀寫都建立新的緩衝區,那樣做會降低效能。
      *               相反,要重用現在的緩衝區,在再次讀取之前要清除緩衝區。
      *             讀取資料:
      *                 呼叫buffer.get(bytes);
     * @Date: 2018/9/12 13:20
     * @author: wgq
     * @version: 1.0
     * @param:
     */
    public void handelerRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();            // 伺服器可讀取訊息:得到事件發生的Socket通道(其實就是這個請求對應的通道)
        ByteBuffer buffer = ByteBuffer.allocate(1024);                    // 建立讀取的緩衝區
        int read = channel.read(buffer);
        if (read > 0) {
            byte[] data = buffer.array();
            String msg = new String(data).trim();
            System.out.println("服務端收到資訊:" + msg);
            ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes());      //回寫資料
            channel.write(outBuffer);                                       // 將訊息回送給客戶端
        } else {
            System.out.println("客戶端關閉");
            key.cancel();
        }
    }
    /**
    * @Description: 啟動服務端進行測試
    * @Date: 2018/9/12 13:26
    * @author: wgq
    * @version: 1.0
    * @param: 
    */
    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.initServer(8000);
        server.listen();
    }
}