socket網絡編程-基礎篇

    學習內容:
    1.學習基本概念,傳輸的同步阻塞式I/O編程,僞異步IO實現
    2.學習基於NIO的同步非阻塞式編程
    3.瞭解基於NIO2.0的異步非阻塞(AIO)編程
    ***********************************************************************************************
    ***
    1.1基本概念
    socket又稱爲“套接字”,應用程序通常通過“套接字”向網絡發出請求或者應答網絡請求。
    ServerSocket與Socket位於java.net包。ServerSocket用於服務器端,Socket是簡歷網絡連接時使用的。在連接成功時,應用程序兩端會產生一個Socket實例,完成這個實例,完成所需的會話。對於一個網絡連接來說,套接字是平等的,不會因爲在服務端或者客戶端而產生不同級別。不管是Socket還是ServerSocket它們的工作都是通過SocketImpl類及其子類完成的。
    套接字之間的連接過程可以分爲四個步驟,服務器監聽,客戶端請求服務器,服務器確認,客戶端確認,進行通信。
    (1)服務器監聽:
     是服務器套接字並不定位具體的客戶端套接字,而是等待連接的狀態,實時監控網絡狀態。
    (2)客戶端請求服務器:
     是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。爲此,客戶端套接字必須首先描述它要連接的服務器的套接字。指出相應的地址和端口。然後向服務端套接字提出連接請求。
    (3)服務器端連接確認:
     是指服務端套接字監聽到或者說接收到客戶端的套接字的連接請求,它就響應客戶端套接字請求,建立一個新的線程,把服務端套接字的描述發給客戶端。
    (4)客戶端連接確認:
     一旦客戶確認了此描述,連接建立好了,雙方開始進行通信,而服務端套接字繼續處於監聽的狀態,繼續接收其他客戶端套接字的連接請求。
    1.2 基本概念
    IO(BIO)和NIO的區別:其本質是阻塞和非阻塞的區別。
    阻塞概念:應用程序在獲取網絡數據的時候,如果網絡傳輸很慢,那麼程序會一直等着,知道傳輸完畢。
    非阻塞概念:應用程序直接可以獲取已經準備就緒好的數據,無需等待。
    IO爲同步阻塞形式,NIO爲同步非阻塞形式。NIO並沒有實現異步,在JDK1.7升級之後支持NIO2.0(AIO-異步非阻塞)。
    同步和異步:一般是面向操作系統和應用程序對IO操作層面上區別。
    同步:應用程序會直接參與IO的讀寫操作,並且我們的應用程序會直接阻塞到某一個方法上。直到數據準備就緒,或者採用輪詢的策略實時檢查數據的就緒狀態。如果就緒則獲取數據。
    異步:IO讀寫操作交給操作系統處理,與我們的應用程序沒有直接的關係。我們的程序不需要關心IO讀寫,當操作系統完成IO讀寫操作時,會給我們應用程序發送通知。我們的應用程序直接拿走數據。
    同步說的是Server服務端的執行方式
    阻塞指的的具體的技術,接收數據的方式、狀態(IO,NIO)。
    1.3 傳統的BIO編程
    網絡編程的基本模型是Client/Server模型。也就是兩個進程直接進行相互通信,其中服務器提供配置信息(綁定IP和監聽端口),客戶端通過連接操作向服務器端監聽的地址發起連接請求,通過三次握手建立連接,如果連接成功,即雙方可以進行通信(網絡套接字SOCKET)。

    缺點:線程較多,服務器就會奔潰。
    解決方案:沒有NIO之前:僞異步
    利用線程池和任務隊列,控制線程個數。
    1.4 僞異步IO
    採用線程池和任務隊列實現僞異步通信框架。

    ***
    2.1 NIO模型介紹
    在介紹之前,先澄清一個概念有人叫NIO爲new IO,有人把NIO叫做Non-block IO,這裏我們還是習慣說後者,非阻塞IO.
    概念:
    Buffer(緩衝區),Channel(管道,通道),Selector(選擇器、多路複用器)

    2.2 Buffer
    Buffer是一個對象,它包含一些要讀取和寫入的數據,在NIO類庫中,加入Buffer對象,體現了新庫和原IO的一個重要區別。在面向流的IO中,可以將數據直接寫入或讀取到Stream對象中。在NIO庫中,所有的數據都是有緩衝區處理的(讀寫)。緩衝區實際是一個數組,通常它是一個字節數組(ByteBuffer),也可以用其他類型的數組。這個數組爲緩衝區提供了數據訪問的讀寫操作屬性,如位置,容量,上限等概念,大家可以參考API文檔。
    Buffer類型:每一種基本數據類型都有(除了Boolean):
      ● ByteBuffer
      ● CharBuffer
      ● ShortBuffer
      ● IntBuffer
      ● LongBuffer
      ● FloatBuffer
      ● DoubleBuffer
    Buffer使用:
    public static void main(String[] args) {
            //1.基本操作
            //2.初始化容量-指定長度10
            IntBuffer intBuffer=IntBuffer.allocate(10);
            intBuffer.put(13);//position:0-->13
            intBuffer.put(13);//position:1-->13
            intBuffer.put(13);//position:2-->13
            intBuffer.flip();//不復位,get()方法拋出異常,復位之後正常遍歷
            intBuffer.put(1,12);
            System.out.println("容量:"+intBuffer.capacity());
            System.out.println("操作空間限制:"+intBuffer.limit());//元素個數決定
            System.out.println("獲取下標爲1的元素:"+intBuffer.get(2));//
            for(int i=0;i<intBuffer.limit();i++) {
                //get()用的是position
                System.out.println(intBuffer.get());
            }
            intBuffer.flip();//不復位,get()方法拋出異常,復位之後正常遍歷
            for(int i=0;i<intBuffer.limit();i++) {
                //get()用的是position
                System.out.println(intBuffer.get());
            }
            //wrap方法使用
            //wrap方法會創建一個數組,這種方法不會初始化緩存的長度,因爲沒有意義。最還會被warp包裹的數組覆蓋掉。
            //並且wrap方法修改緩衝區的時候,數組本身也會跟着變化。
            int[] arr=new int[] {1,2,5};
            IntBuffer buf=IntBuffer.wrap(arr,0,3);
            System.out.println(buf);

            //複製方法
            IntBuffer buf2=intBuffer.duplicate();
            buf2.position(1);
            //設置position:java.nio.HeapIntBuffer[pos=0 lim=3 cap=3]f
            System.out.println(buf2);
            //放到數組中
            int[] arr2=new int[buf2.remaining()];
            buf2.get(arr2);
            for(int a: arr2) {
                System.out.println(a+"+");
            }
            //2
            buf2.flip();
            System.out.println("可讀區域"+buf2.remaining());
            //arr方式
            IntBuffer intBuffer2=IntBuffer.allocate(10);
            intBuffer2.put(arr);
            System.out.println(intBuffer2);
        }
    2.3 Channel
    通道(Channel),它就像來自水管一樣,網絡數據通過Channel讀物和寫入。通道與流不同的之處在於通道是雙向的。而流只有一個方向移動(要麼是InputStream,要麼是OutputStream)。而通道可以用於讀、寫,或者同時進行,最重要的是可以與多路複用器結合起來,有多種狀態位,方便多路複用器去識別。事實上,通道分爲兩類:一類是網絡讀寫的(SelectableChannel),一類是用於文件操作的(FileChannel)。我們用的SocketChannel和ServerSocketChannel都屬於SelectableChannel的子類。
    2.4 Selector
    多路複用器(Selector),他是NIO編程的基礎,非常重要。多路複用器提供選擇已經就緒的任務的能力。
    簡單說,就是selector會不斷地輪詢註冊在其上的通道(Channel),如果某個通道發生了讀寫的操作。這個通道就處於就緒狀態,會被Selector輪詢出來,然後SelectionKey可以取得就緒的Channel的集合,從而進行後續的IO操作。
    一個多路複用器(Selector)可以負責成千上萬的Channel通道,沒有上線,這也是JDK使用epoll代理傳統的select實現。獲得連接句柄沒有限制。這也就意味着我們只要一個線程負責Selector的輪詢,就可以接入成千上萬的客戶端,這是JDK的NIO庫的巨大進步。
    Selector線程就類似一個管理者(Master),管理了成千上萬個管道,然後輪詢哪個管道的數據已經準備好,通知CPU執行IO的讀寫操作。
    Selector模式會分配給每個管道一個key值。相當於標籤,select選擇器就是以輪詢的方式查找註冊所有的IO事件(管道),當我們的IO事件(管道)準備就緒後,select就會識別,會通過key值來找到相應的管道,進行相關的數據處理操作(從管道里讀和寫數據,寫到我們數據的緩衝區中)。
    每個管道都會選擇器進行註冊不同的事件狀態。以便選擇器查找。
    SelectionKey.OP_CONNECT
    SelectionKey.OP_ACCEPT
    SelectionKey.OP_READ
    SelectionKey.OP_WRITE
    ***
    3.1 NIO2.0(AIO)模型介紹
    AIO編程,在NIO基礎之上引入了異步通道的概念,並提供了異步文件和異步套接字通道的實現。從而在真正意義上實現了異步非阻塞,之前我們學習的NIO只是非阻塞而並非異步。而AIO它不需要通過多路複用器對註冊的通道進行輪詢操作即可實現異步讀寫。從而簡化了NIO編程模型。也可以稱之爲NIO2.0,這種模式才真正的屬於我們異步非阻塞的模型。
    AsynchronousServerScoketChannel
    AsynchronousSocketChannel
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章