netty1---傳統IO和NIO的區別


傳統IO;

package OIO;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 傳統socket服務端
 */
public class OioServer {
    @SuppressWarnings("resource")
    public static void main(String[] args) throws Exception {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //創建socket服務,監聽10101端口
        ServerSocket server=new ServerSocket(10101);
        System.out.println("服務器啟動!");
        while(true){
            //阻塞
            final Socket socket = server.accept();//有一個客戶端進來就向下執行,長連接的服務器。
            //用線程池可以有多個客戶端連接,但是非常消耗性能
            //tomcat里面來一個請求就開一個線程,當一問一答結束,服務端主動關閉,那么這個線程就可以為其他請求服務了。
            System.out.println("來個一個新客戶端!");
            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    handler(socket);
                }
            });
        }
    }
    
    /**
     * 讀取數據
     */
    public static void handler(Socket socket){
            try {
                byte[] bytes = new byte[1024];
                InputStream inputStream = socket.getInputStream();//獲取輸入流
                while(true){
                    //阻塞
                    int read = inputStream.read(bytes);
                    if(read != -1){// -1就是客戶端關閉了。
                        System.out.println(new String(bytes, 0, read));
                    }else{
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                try {
                    System.out.println("socket關閉");
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
}

NIO:

package 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;

/**
 * NIO服務端
 */
public class NIOServer {
    // 通道管理器
    private Selector selector;
    /**
     * 獲得一個ServerSocket通道,並對該通道做一些初始化的工作
     */
    public void initServer(int port) throws IOException {
        // 獲得一個ServerSocket通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 設置通道為非阻塞,只能是非阻塞的。
        serverChannel.configureBlocking(false);
        // 將該通道對應的ServerSocket綁定到port端口
        serverChannel.socket().bind(new InetSocketAddress(port));
        // 獲得一個通道管理器
        this.selector = Selector.open();
        // 將通道管理器和該通道綁定,並為該通道注冊SelectionKey.OP_ACCEPT事件,注冊該事件后,
        // 當該事件到達時,selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞。
        //注冊請求連接進來的key : OP_ACCEPT
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理
     */
    public void listen() throws IOException {
        System.out.println("服務端啟動成功!");
        // 輪詢訪問selector
        //客戶端連接進來和發送數據的請求,都走這里,這里是死循環。
        while (true) {
            // 當注冊的事件到達時,方法返回;否則,該方法會一直阻塞,
            
            //客戶端連接進來和發送數據的請求,都走這里,阻塞
            selector.select();//select是C實現的,多路復用的實現。
            
            // 獲得selector中選中的項的迭代器,選中的項為注冊的事件
            Iterator<?> ite = this.selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();
                // 刪除已選的key,以防重復處理
                ite.remove();
                handler(key);
            }
        }
    }

    /**
     * 處理請求,客戶端連接進來和客戶端發數據,走這里。
     */
    public void handler(SelectionKey key) throws IOException {
        // 客戶端連接進來
        if (key.isAcceptable()) {
            handlerAccept(key);
            // 客戶端發數據
        } else if (key.isReadable()) {
            handelerRead(key);
        }
    }

    /**
     * 處理連接請求
     */
    public void handlerAccept(SelectionKey key) throws IOException {
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        // 獲得和客戶端連接的通道,
        SocketChannel channel = server.accept();
        // 設置成非阻塞
        channel.configureBlocking(false);
        // 在這里可以給客戶端發送信息哦
        System.out.println("新的客戶端連接");
        // 在和客戶端連接成功之后,為了可以接收到客戶端的信息,需要給通道設置讀的權限。
        //注冊接收消息的key : OP_READ
        channel.register(this.selector, SelectionKey.OP_READ);
    }

    /**
     * 處理讀的事件
     */
    public void handelerRead(SelectionKey key) throws IOException {
        // 服務器可讀取消息:得到事件發生的Socket通道
        SocketChannel channel = (SocketChannel) key.channel();
        // 創建讀取的緩沖區
        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();
        }
    }

    /**
     * 啟動服務端測試
     */
    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.initServer(8000);
        server.listen();
    }
}

 

    Netty入門教程
第一天內容    傳統IO與NIO比較


傳送IO特點
=======================分割線==========================

NIO的新API

ServerSocketChannel,對應傳統IO的:    ServerSocket

SocketChannel,對應傳統IO的:    Socket

Selector:NIO核心的東西,負責監聽ServerSocketChannel、SocketChannel。NIO是可以實現單線程為多個客戶端服務的。傳統IO是做不到的, 傳統IO要多線程才行。

SelectionKey:監聽的事件。


NIO的一些疑問

1、客戶端關閉的時候會拋出異常,死循環
解決方案
        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();
        }

2、selector.select();阻塞,那為什么說nio是非阻塞的IO?

    selector.select()
    selector.select(1000);阻塞1秒,1秒還沒有請求過來就返回了,
    selector.wakeup();也可以喚醒selector
    selector.selectNow();也可以立馬返還,視頻里忘了講了,哈,這里補上


3、SelectionKey.OP_WRITE是代表什么意思:OP_WRITE表示底層緩沖區是否有空間(事件的觸發條件),是則響應返還true,一般不注冊這個事件。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM