您好,登錄后才能下訂單哦!
傳統BIO網絡編程知識點與Java NIO分別是怎樣的,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
本來想把這部分內容加在“Netty高并發編程與性能調優實戰經驗分享”這篇的,但是怕大家看了前這篇的內容,就沒有看下去的欲望了,哈哈。
傳統BIO編程知識點總結
Java NIO簡介
下圖為我用印象筆記所做的《Netty高并發編程與性能調優》思維導圖。這是我對網絡編程的總結吧,我算是很早就開始接觸Socket編程的,在大學就學過Socket編程,雖然那時候學的是C#,但原理是一樣的,不分語言。
我參加過中國軟件杯,做的就是一個“同步手繪板”應用,使用Socket實現移動端和PC端同步繪制,雖然沒得獎。專科實習的時候,做過一個監控攝像頭設備的系統,比如遠程控制拍照角度、自動拍照、獲取電量等。從第一家公司辭職之后,也自己做了一個模仿微信的聊天系統。升本期間,了解到有NIO,Netty這些云云的存在,也跟風學了一把,后面畢設也用Netty實現一個戀愛APP的私密聊天功能的服務器。對BIO、NIO也算有點了解,最近為了項目的性能調優,也啃了好久的Netty的源碼,雖然現在也只看懂了些皮毛,不過對本次性能調優還是非常有幫助的。
我對傳統BIO編程的一點總結,幾個我認為最重要的知識點。
知識點一:Socket套接字復用池
不管任何一門高級語言,Socket編程都是服務端一個線程處理客戶端的一個連接,因為讀寫都是阻塞的。為避免頻繁的線程創建和消毀,都會使用線程池來實現線程的復用。對于BIO,一邊會根據服務器硬件配置估算服務所能并發處理的最大連接數,據此設置線程復用池的大小。
知識點二:內存緩存池
用于接收和發送字節數據的緩存區,也是用于避免頻繁向系統申請和釋放內存。對應的,Netty也有緩存池的概念,相對復雜些,分直接內存和堆內存兩種,直接內存就是jvm堆外內存,不被jvm所管。
知識點三:消息隊列,解析數據包
有了內存緩存池,為啥還要有個消息隊列。服務端讀取到客戶端發送過來的消息,可能不是一個完整的數據包,所以就需要對接收到的字節數據做解析,根據所使用的協議去解析字節數據,解析成一個個完整的數據包。
知識點四:自定義通信協議
做為后端開發人員,我們最熟悉不過的就是HTTP協議了。使用Socket編寫網絡程序,我們可以自定義通信協議,這相當的好玩。自定義協議可以避免別人識別你的通信協議攔截數據包分析,也可對數據包進行加密傳輸。自定義協議的數據包體積小,可跟據業務需求修改。
知識點五:心跳保活
NIO與BIO的區別:
BIO: 同步阻塞式IO,服務器需要為每一個客戶端創建一個線程處理連接。
NIO:同步非阻塞式IO,服務端可以使用一個或多個線程監聽客戶端的連接請求,并將連接注冊到多路復用器Selector上,使用Selector輪詢I/O就緒事件,當監聽到有就緒事件時,才會為準備就緒的連接開啟一個線程去處理。
NIO
Channel:channel是一個通道,可以通過它讀取和寫入數據。對于網絡編程而言,網絡數據通過channel接受客戶端發來的消息,也可以通過channel向客戶端發送消息,channel是全雙工的,對應的類分別為SocketChannel、ServerSocketChannel。
ServerSocketChannel: 用于監聽TCP連接的通道,類似于ServerSocket。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 綁定服務端監聽端口serverSocketChannel.socket().bind(new InetSocketAddress(this.port), 1024);// 監聽客戶端連接SocketChannel socketChannel = serverSocketChannel.accept();
SocketChannel:用于TCP網絡連接的通道。實現與客戶端數據傳輸。可以設置為非阻塞。類似于Socket。
Socket于Channel的區別:socket數據流是單向的,客戶端與服務器實現雙向通信需要一個Input流和一個Output流,一個用于接收數據,一個用于發送數據。而Channel是全雙工的,即可以接受客戶端發來的數據,也可以向客戶端發送數據。
Selector:選擇器,或多路復用器。用于輪詢檢查一個或多個NIO 通道(Channel)的狀態。對于網絡編程而言,Socket有四種狀態,監聽連接、連接準備就緒、讀準備就緒、寫準備就緒,對應的SelectorKey的取值如下。
SelectorKey:
OP_READ = 1 << 0; 0000 0001OP_WRITE = 1 << 2; 0000 0100OP_CONNECT = 1 << 3; 0000 1000OP_ACCEPT = 1 << 4; 0001 0000
I/O多路復用:I/O指的是網絡I/O,多路指多個TCP連接(BIO: socket, NIO:channel),復用指的是只用一個或多個線程處理事件。總的來說,就是使用一個或多個線程處理多個TCP連接。不再像BIO的一個連接一個線程處理,NIO則可以只用一個線程處理所有連接,也可以使用n個線程處理所有連接。
Channel通過register方法與Selector多路復用器綁定,并指定自己感興趣的事件,比如
channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
綁定后生成一個SelectionKey,SelectionKey持有Channel。
通過輪詢監聽,通過Selector的select()方法可以選擇已經準備就緒的通道,這些通道包含你注冊的事件。比如你對讀、寫就緒的通道感興趣,那么select()方法就會返回讀事件已經就緒的那些通道和寫事件已經就緒的那些通道。select方法是一個阻塞方法,至少有一個通道在你注冊的事件準備就緒時,才會返回。返回結果為準備就緒的SelectionKey總數。
當監聽到有事件準備就緒時,再通過Selector的selectedKeys()獲取準備就緒的SelectionKey集合。通過遍歷處理事件。處理完后需要移除,否則下次selectedKeys()還會重復拿到。
for (;;) { try { // 獲取到之后返回總數,否則線程將處于阻塞狀態 int readyCount = this.selector.select(); if (readyCount == 0){ continue; } // 獲取當前所有準備就緒的SelectionKey Set<SelectionKey> readyKeys = this.selector.selectedKeys(); for (SelectionKey selectionKey : readyKeys) { // 需要注冊新的感興趣事件 handleEvent(selectionKey); // 處理完要移除,否則下次selectedKeys還是能拿到 readyKeys.remove(selectionKey); } } catch (IOException e) { e.printStackTrace(); }}
select方法底層通過調用native方法實現事件監聽,在linux上,就是調用系統的epoll方法,網卡設備對應一個中斷信號, 當網卡收到網絡端的消息的時候會向CPU發起中斷請求, 然后CPU處理該請求。通過驅動程序進而操作系統得到通知,系統再通知epoll,epoll通知用戶代碼。
關于傳統BIO網絡編程知識點與Java NIO分別是怎樣的問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。