服務器_網絡通信(TCP/IP網絡模型,HTTP(URL),Socket,WebSocket,BIO,NIO,AIO,Mina框架,Netty框架)

通信協議

  • 客戶端和服務器之間通信所需要遵循的某種規則

網絡模型OSI(Open System Interconnnection)

  • 該模型把把網絡通信分爲7層(物理層,數據鏈路層,網絡層,傳輸層,會話層,表示層,應用層),是設計和描述計算機網絡通信的基本架構。
  • 在實際應用中一般只是用到4層。

TCP/IP網絡模型

這個模型是一系列網絡協議的總層,是互聯網的基礎

4個層次

  • 鏈接層
    • 對應的是物理層和數據鏈路層,主要負責監視數據在主機和網絡之間的交換
    • 主要協議
      • 地址解析協議(ARP)
  • 網絡層
    • 對應的是網絡層,主要解決主機與主機之間的通信問題,所含網絡通信協議設計數據包在網絡行的邏輯傳輸。還負責數據包在多種網絡之間的路由。
    • 主要協議
      • 網際協議(IP)
      • 互聯網管理協議(IGMP)
      • 互聯網控制報文協議(ICMP)
  • 傳輸層
    • 對應傳輸層,爲應用實體提供端對端的通信功能,保證數據包的順序傳送和數據的完整性。
    • 主要協議
      • 傳輸控制協議(TCP)
      • 用戶數據報協議(UCP)
  • 應用層
    • 對用會話層,表示層,應用層,提供用戶所需的各種服務
    • 主要協議
      • FTP
      • Telnet
      • DNS
      • SMTP
      • HTTP
      • Socket

這些通信協議,保證了端對端網絡通信的流暢,常用的TCP和UDP協議是傳輸層協議,區別很大

1. 面向連接的TCP

一種基於連接的協議,在雙方通信傳輸數據之前,必須建立可靠的連接。(如雙方通話前必須把電話接通)

  • 通過三次對話建立連接(三次”握手“)Client A,Server B
    在這裏插入圖片描述
    1. 客戶端A詢問服務器B: 發送請求建立連接數據包
    2. 服務器B應答客戶端A:返回同意或者拒絕建立連接的數據包
    3. 若服務器同意,客戶端則發送建立連接數據包,拒絕則會話結束。

經過上述的三次握手,雙方建立了一條隱形的通信通道,這樣雙方的連接就不會斷開,從而進行收發信號。

  • 特點
    • 面向連接:通信前要三次”握手“建立連接。
    • 安全可靠:每一次通信都必須得到對方的應答,否則認爲數據報丟失,需要重發。
    • 全雙工通信:一旦建立連接,雙方都可以通過通道進行數據傳輸。
    • 一對一:通信只能建立在兩個點之間
    • 面向流通信: 通信傳輸是通過流的形式進行。

2. 面向數據報的UDP

無需建立連接,只要指定目標地址,即可通過UDP向目標地址發送數據報。
由於沒有建立可靠的連接,不保證數據包可以送到目的地,所以數據報可能丟失。
比TCP可以發送更大的數據報,並進行一對多的廣播發送。

  • 特點:
    • 無連接:通信之前無需建立可靠的連接
    • 數據無保障: UDP不對數據排序,數據報文頭部無報文順序信息,而且無需按順序到達,可能造成報文混亂。
    • 開銷小:無連接,不保證報文送達和報文順序,開銷較小,而且速度更快。
    • 一對一,一對多,多對多:無需連接,可以進行一對一通信,也可以進行一對多的廣播通信和多對多的通信。

UDP和TCP的比較和應用場景

  • 大多數情況下,如登錄,支付,上傳等,都需要服務器返回具體的執行結果以及判斷是否成功,需要TCP。
  • 在電視直播中,如果每一個幀或者幾個幀畫面都要直播服務器確認,會使得畫面卡頓和一直佔用網絡帶寬,應使用UDP。

3.HTTP編程

超文本傳輸協議,是應用最廣泛的網絡協議,幾乎所有的www 文件都要遵守這個協議的標準。最初是爲了提供一種HTML頁面發佈和接收的方法。
在這裏插入圖片描述
工作原理

  1. 客戶端請求服務器建立連接併發出請求數據
  2. 服務器接收請求併發出應答數據,服務器接收到並進行處理,處理之後就返回數據並斷開連接。
    在這裏插入圖片描述
    拓展知識:
  • URL和URN 是URI的子集, 最常用的是URL。
  • URI : Universal Resource Identifier 統一資源標識符
    • 在某一規則下能把一個資源獨一無二地標誌出來。(像人們的身份證號一樣,確定一個唯一的人)
    • Web上,假設所有的Html文檔都有唯一的編號,記作html:xxxxx,xxxxx是一串數字,即Html文檔的身份證號碼,這個能唯一標識一個Html文檔,那麼這個號碼就是一個URI。
    • URI一般由三部組成
      ①訪問資源的命名機制
      ②存放資源的主機名
      ③資源自身的名稱,由路徑表示,着重強調於資源。
  • RUL :Universal Resource Locator 統一資源定位符
    • 是一個地址,唯一確定一個資源(括文件、服務器的地址和目錄等),在Web上通過描述是哪個主機上哪個路徑上的文件來唯一確定一個資源,也就是定位的方式來實現的URI。
  • URN : Universal Resource Name 統一資源名稱

訪問網站的範例

  1. 客戶端在瀏覽器輸入網址,按回車鍵,瀏覽器封裝URL到HTTP請求體併發送請求包給Web服務器。
  2. Wub服務器接收請求後建立連接,將請求體中的head和body體中的信息經過服務器邏輯處理後,返回HTML頁面給客戶端。
  3. 客戶端接收到服務器返回信息結構體,從中讀取HTML代碼,斷開連接。
  4. 服務器通過瀏覽器解析頁面並呈現在瀏覽器上。

使用Java的net包進行簡單的HTTP請求

  1. 接收外界傳入的目的網站
  2. 的網址字符串根據網址創建包含地址信息的URL對象
  3. 將URL強制類型轉換獲取HttpURLConnection對象
  4. 然後對對象進行連接信息設置
  5. 獲取到輸入流,用流對象讀取服務器傳過來的相應內容。
  6. 在對應答數據執行客戶端的業務邏輯

標誌的HTTP支持六種請求方法:GET,POST,HEAD,PUT,DELETE,OPTIONS,最常用的是GET和POST方法。

GET和POST

  • 客戶端
    • GET請求默認HttpURLCnnection的設置
      1. 接收目的服務器的網絡地址字符串
      2. 構造URL對象
      3. 設置爲GET請求方式
      4. 發起連接
      5. 創建流對象並從通道中讀取服務器的應答信息
      6. 執行業務邏輯
    • POST請求
      1. 接收目的服務器的網絡地址字符串
      2. 調用HttpURLConnection的setRequestMethod方法爲POST,並將SetDoOutput爲true,並通過write方法寫入參數到body體。(因爲消息POST消息必須通過body傳遞參數)
  1. 服務端
    • 添加一個類,Server繼承servlet,重寫doGet 和 doPost 方法分別接收GET和POST,最後在web端添加此Servert的部署。

4,Socket 編程

  • socket(套接字)用於描述IP地址和端口,是個通信鏈的句柄,可以用來實現不同虛擬機之間或者不同計算機之間的通信。網絡上的主機一般會運行多個服務器,每個服務器上的每一種服務都會打開一個Socket並綁定到一個端口,不同端口對應着不同的服務。

  • IP地址和端口號組成了Socket,Socket 是網絡上運行的程序之間雙向通信鏈路的終結點,是TCP和UDP協議的基礎。

    • IP對應着網絡上的計算機,而端口對應着計算機上某個具體的進程或者服務。(就好像寄信一樣,IP地址對應着具體的居民房,端口則對應於人名,根據地址和人名就能實現寄信)
  • 根據連接啓動的方式和本地Socket要連接的目標,Socket之間連接分爲三個步驟:

    1. 服務器監聽、
    2. 客戶端請求
    3. 連接確認。
  • 服務器監聽

    • 服務器端Socket只需要在程序啓動之後處於等待連接的狀態,並實時監控網絡狀態,等待其他客戶端Socket連接。
  • 客戶端請求
    客戶端Socket需要先創建一個Socket,並在Socket中描述服務器Socket的信息(IP地址和端口號),然後向服務器Socket提出連接請求。

  • 連接確認
    * 當服務器Socket監聽到或者接到客戶端Socket的連接請求時,就會響應客戶端Socket的請求,建立一個新的線程,把服務器Socket的描述發送給客戶端,一旦客戶端確認此描述,連接就建立好。從此這個線程就爲該客戶端服務。
    * 服務器的Socket 繼續處於監聽狀態(主線程),繼續接收其他客戶端的Socket的連接請求,接收其他客戶端Socket的連接請求。

在這裏插入圖片描述
TCP服務端

  • 設置獨立存在的端口號,創建一個服務端Socket綁定端口並啓動,進入等待狀態等待用戶連接。
  • 堵塞方式accept()方法接收並讀取客戶端發來的流信息
  • 從Socket的緩衝區中讀取信息,讀取完畢後,處理事務
  • 將處理結果發送到Socket的緩衝區,再傳送到客戶端的Socket的緩衝區,等待用戶讀取。
  • 釋放Socket資源。
  • 循環等待下一個連接

TCP客戶端

  • 客戶端通過帶有服務器IP和端口號的Socket類對象連接到服務器,建立連接。
  • 發請求信息寫入Socket緩存區,發送到服務器
  • 再讀取服務器的應答信息
  • 關流,釋放資源

UDP發送端

  • 創建發送方(自己)的Socket對象,指定端口
  • 構造數據包(發給指定主機上的指定端口號)
  • 發送數據包給目的接收者
  • 接收目的接收者返回的數據
  • 釋放資源

UDP接收端

  • 創建接收方(自己)的Socket對象(該socket對象綁定了的當前進程的IP地址和端口)。
  • 構造接收數據包,存儲在字節數組
  • receive()方法,堵塞等待接收數據包,直到接收到爲止
  • 獲取接收到的數據包,從中獲取發送者的各種信息(IP,消息文本,端口)
  • 處理事務,構造應答數據包,指定目的接收者的socketaddress地址(告訴他接收完畢)
  • 發送數據包
  • 關閉資源。

WebSocket

  • 是一種隨着HTML5的興起而出現的新協議,在它之前,只能通過HTTP實現 單通道通信,而WebSocket實現了瀏覽器與服務器之間的全雙工通信,它的通信方式類似TCP的連接方式,先通過“握手”協議通信,然後再建立通信傳輸數據,可以實現服務器對客戶端的消息推送。
  • 在WebSocket之前,要在Web開發中實現即時通信,會採用輪詢方式:在特定的時間間隔內不斷髮出HTTP請求來請求服務器的數據,然後服務端返回客戶端請求時的最新數據給瀏覽器。但是每一個請求都有一個很長的header頭部信息,很佔用帶寬。而WebSocket協議佔用很小的header,並可以實現服務器主動推送。

服務端

重寫OnOpen,OnClose,OnMessage三個方法是實現WebSocket服務端的建立連接,斷開連接,消息接收功能,並在消息接收中實現自己的業務邏輯。也可以主動推送消息給客戶端。

HTML5客戶端

創建WebSocket類對象,同樣重寫OnOpen,OnClose,OnMessage三個方法是實現WebSocket客戶端的建立連接,斷開連接,消息接收功能。

總結:以上是HTTP,Socket,WebSocket 的思路和Java實現的理解,不過在實際項目中,要考慮很多問題,如HTTP響應超時,頭部消息設置,Socket,WebSocket的心跳機制,斷線重連機制,或者多線程下的網絡編程等。

Java NIO基礎

網絡編程模型基礎

  • 同步
    • 用戶進程觸發IO操作並等待或者輪詢去查看IO操作是否就緒。(如發送方和接收方處於同一時鐘,發送多少接收多少,協調合作。如果IO還沒輪到當前進程使用,則當前進程一直等待IO的準備就緒)
  • 異步
    • 用戶進程觸發IO操作以後做其他操作,當IO操作完成後會得到IO完成的通知。

阻塞和非阻塞是進程在訪問數據的時候,根據IO的操作的就緒狀態來採取的不同方式,實際就是執行數據操作之後是否立即返回值。

  • 阻塞
    *用戶進程訪問數據時,如果尚未就緒,當前線程就一直等待數據。(如函數需要返回數據,但是數據還沒讀取完畢,阻塞直到將數據返回才執行其他的函數)
  • 非阻塞
    • 用戶進程訪問數據時,數據未準備就緒時,當前線程可以做其他事情,間隔一段時間檢查數據是否準備就緒。(函數需要返回值,但是並不等待,繼續往下執行以下的函數)

區別

  • 同步和異步對應消息通知機制,指線程發起一個功能調用的時候是否立即返回結果,主要針對IO操作
  • 阻塞和非阻塞對應等待消息通知的狀態,指線程在得到調用結果前是否掛起,主要針對線程。

1,BIO編程(Blocking-IO 阻塞式IO)

BIO是同步阻塞型IO,在服務器的實現模式:每一個連接都要對應一個線程,當接收到客戶端的請求時,服務器需要啓動一個新的線程與之交互。

  • BIO通信模式要使用連接數目比較少且固定的服務器框架,並要求服務器有較好的資源。
  • 缺陷:當新的線程不做任何處理時處於掛起狀態,會給服務器造成不必要的線程開銷。
    在這裏插入圖片描述
    BIO經典模型 客戶端-服務器模型
  • 傳統的網絡編程中,服務器綁定IP並監聽端口,客戶端通過IP和端口與服務器建立連接,雙方就可以通過Socket進行通信,ServerSocket 負責綁定IP地址並監聽獨立端口,Socket就發起連接請求,接收器負責接入請求,連接成功後雙方就可以通信,服務器端創建一個新線程,處理客戶端請求,再通過輸出流返回給客戶端,銷燬線程,釋放資源。
  • 這種IO稱爲同步阻塞式IO,每當一個客戶端接入,服務器就建立一個新線程與之交互,線程數與客戶端數相同,當線程增多而高併發,整個BIO網絡程序就會佔用大量JVM線程,導正服務器性能下降,甚至跑出堆棧溢出的異常,最終程序崩潰。
  • 典型應用就是TCP協議通信方式: 服務器通過accept() 方法實現阻塞,直到有客戶端的請求到達。

2,NIO編程(Non-Blocking IO 非阻塞式IO)

特點

  • 創建一個線程負責處理IO事件和IO事件的分發。
  • 事件驅動機制非同步監聽事件,而是事件到達後觸發事件
  • 線程之間通過wait,notify等方式通信,減少了線程切換。

NIO是同步非阻塞型IO,服務器實現模式爲:一個請求一個線程,客戶端發送的連接請求都會註冊到多路複用器上,多路複用器輪詢到連接有IO請求時才啓動一個線程進行處理。適用於連接數目多且連接比較短(輕操作)的架構(如聊天服務器),併發限於局部,編程複雜。

原理

  • 多個客戶端向服務器發送連接請求,服務端將請求註冊到Rector多路複用器,它查詢到有IO請求時將啓動一個線程,將其分發到該線程處理。每個線程的處理流程都相同:讀取數據,對數據編碼,進行計算處理,處理後將響應消息編碼,將其發送出去給客戶端。在這裏插入圖片描述
    在這裏插入圖片描述
  • NIO服 務端只需啓動一個專門的線程來處理所有的IO事件,採用雙向通道(channel)進行數據傳輸,在通道上註冊感興趣事件(服務端接收客戶端連接事件,客戶端連接服務端事件,讀事件,寫事件),即事件驅動機制。
  • NIO的服務端和客戶端都需要維護一個管理通道的對象(selector),該對象能檢測到一個或者多個channel的事件,服務器的selector上註冊了事件,當某時刻,客戶端給服務器發送一些數據,阻塞IO會調用read()方法阻塞地讀取數據,而NIO的服務端會在selector中添加一個讀事件。添加事件後,服務端的處理線程會輪詢訪問selector(遍歷是否有事件觸發),如果訪問時發現有感興趣的事件到達,則觸發事件,沒有事件到達,處理線程就一直阻塞到感興趣的事件到達爲止。

服務端
主方法:(1)創建服務器對象(2)初始化服務器,(3)監聽端口
1,初始化服務器方法:

  • 獲取一個通道,設置爲非阻塞,
  • 將通道對應的ServerSocket綁定端口
  • 獲取一個通道管理器,爲通道註冊接收用戶請求的事件(當事件到達時,listen()中的selector.select()會返回,否則它會一直等待。)
    2,監聽方法:輪詢一個selector,如果有事件到達則立即處理
  • while(true)實現阻塞,直到有事件到達才返回
  • 每一次輪詢selector,獲取它中的選中的項(註冊的事件)的迭代器
  • 當有請求事件到達則獲取和客戶端連接的通道,給客戶端發信息,併爲通道設置讀權限
    3,服務器讀取方法
  • 獲取事件發生的Socket通道
  • 創建讀取的緩衝區
  • 從通道中讀取數據到緩衝區字符串,並將其轉換爲字節數組
  • 處理數據後,把應答信息寫回通道,,回送客戶端。

客戶端

主方法:創建客戶端對象,初始化對象,監聽端口

1, 初始化方法

  • 獲取Socket通道,並設置爲非阻塞
  • 獲取通道管理器
  • 客戶端連接服務器(完成一部分,要在listen才能完全完成)
  • 將管理器綁定通道並註冊連接服務器事件

2,輪詢監聽通道管理器方法:

  • while(true)循環中
  • 調用select()方法選擇一組I/O操作的事件置於管理器(該方法不阻塞,當一個通道被選中時,seletor的wakeup方法被調用而返回,對於客戶端,通道一直都是被選中的)
  • 獲取通道管理器的迭代器,遍歷每個註冊的事件,看是否有事件到達
  • 有連接事件則獲取通道並設置非阻塞,向服務器發送消息
  • 連接成功後,賦予通道可讀權限,讀取服務器返回的數據

3,讀取服務器信息方法

  • 獲取通道對象,讀取數據到緩衝區
  • 處理信息並將應答信息寫入通道中。

3.2.3AIO編程(Async IO/NIO.2,異步IO)

異步阻塞型IO,在服務端的實現方法通常是一個有效的請求對應一個線程,來自客戶端的I/O請求都是由OS先完成再通知服務器應用去啓動線程進行處理,使用於連接數據較多和比較長的架構。如相冊服務器,充分調用OS參與併發操作。

服務端類
在這裏插入圖片描述
主方法:創建服務器對象,調用listen方法。

  • 1,構造方法
  • 2,listen()方法
    • 打開一個服務通道,綁定服務端口
    • 通道對象通過accept()方法綁定一個消息處理類
    • 創建新線程打印消息
    • 啓動新線程
  • 3,accept到一個請求時的回調類
    • completed ()連接並讀取客戶端信息
    • 調用read()函數,調用請求數據的回調類
    • 遞歸調用,監聽新的請求
  • 4,Read到請求數據的回調類
    • 構造函數
    • completed()方法,讀取請求,處理數據,響應結果
    • write()方法,調用響應完請求的回調類
  • 5,Write響應完請求的回調類
    • 構造函數
    • completed()方法,清理緩衝區,關閉服務端
    • fail() ,失敗後關閉服務端
  • 6,close()方法 ,關閉通道

客戶端
使用TCP的客戶端

  • 總結:服務端啓動之後,沒有阻塞等待客戶端的連接,而是做其他事情;服務端也不用等待IO操作完成再去執行其他操作,只需要IO操作交給操作系統,然後去其他操作,等IO操作完了,系統會回調通知結果,大大節省了系統的內存時間。

Mina

Apache Mina時基於Java NIO 的網絡應用框架,底層基於TCP/IP和UDP/IP的,是一款協議棧的通信框架,可以快速開發高性能,高擴展性的網絡應用。
Mina提供了事件驅動和 異步操作的模型,IO默認是java NIO 作爲底層支持。

總體架構

在這裏插入圖片描述

IoService

  • Mina的最底層,負責底層的IO的相關工作。如IoSocketAcceptor和IoSocketChannel ,對應着TCP下的服務端與客戶端的IoService(隱藏了所有底層的細節,只對上層提供統一的基於事件驅動的異IO接口)。
  • 當有數據到達時,IoService 會調用底層的IO接口讀取數據並封裝爲IoBuffer,然後再以事件的形式通知上層代碼,將Java NIO 的同步IO接口轉換爲異步IO。

IoFilterChain

  • 代碼分離是Mina的設計之一,Mina的業務代碼和數據處理分別負責不同的內容。
  • 開發者的業務代碼只需要專注於業務邏輯,而他無關的邏輯(數據包的解析,過濾,封裝等),交給IoFilterChain處理。
  • FilterChain 是其處理流程的擴展點,使得Mina的結構劃分更清晰,代碼分工明確。開發者要增加處理流程而不不影響後續的業務邏輯代碼,只需要向Chain添加IoFilter。

IoHandler

InHandler實現業務邏輯,開發時,開發者需要自己實現IoHandler接口,是Mina處理流程的終點,每個IoService都要指定一個IoHandler。

IoSession

  • Mina底層的連接封裝成IoSession,每個IoSession對應一個客戶端與服務端的底層IO連接。開發者需要管理好每一個客戶端的IoSession,通過它可以獲取連接的相關信息,向客戶端發送數據。
  • 通過IoSession發送數據是一個異步過程,發送操作首先通過IoFilterChain到達 IoService,然後IoService將發送操作封裝成WriteRequest,並放入Session的writeRequest中,最後交給IoProcessor統一調度flush發送出去,整個發送操作不會引起調用線程阻塞。

工作原理

在這裏插入圖片描述

Acceptor與Connector線程

  • 服務端綁定端口之後,Mina就創建Acceptor線程,專門負責監聽,這個線程的工作就是調用 java NIO 的 select connect 事件 ,在獲取新建立的連接之後,將去封裝成IoSession,並交給IoProcessor線程處理。
  • 在客戶端會有一個對應的Connector線程,兩個線程是一對一關係。
  • 一旦建立,外界則無法控制其線程的數量,直到連接斷開。

Processor線程

  • Processor 線程主要負責IO讀寫操作與執行IoFilterChain和IoHandler邏輯,它默認會有CPU數量+1個線程,並且這個數量可以通過配置參數進行控制,每一個進來的IoSession都會分配到這些線程中,默認策略是session id 絕對值對 N 取模 來分配。
  • Processor線程維護一個selector,並對維護着的IoSession 進行select 和遍歷,然後讀取數據,以事件的形式通知IoFilterChain,並對請求隊列進行flush操作。
  • 把IoSession均分到多個Processor線程中進行處理,可以充分利用計算機多核的處理能力,減輕select操作的壓力。雖然默認的Processor的線程數量足夠滿足大部分情況下的需求,實際項目中需要根據實際線上環境修改。

線程模型

  • 每一個Processor的線程內部看,IO請求的處理是按順序處理中,即按照單線程進行處理。
  • 當Processor線程select到一批就緒的IO請求後,就會在線程內部遍歷並對這些請求一一處理(處理流程包括IoFilter和IoHandler的邏輯)
  • 當就緒IO前面的IO請求處理完畢,纔會取出下一個請求進行處理。當IoFilter和IoHandler的邏輯有耗時操作時,Processor會被阻塞,後續的請求將不會處理。高併發的Mina使用線程池解決這個問題。

線程三種模型

  1. 單線程模型
    Mina默認線程模型,Processor處理了從底層IO到上層IoHander邏輯所執行的所有工作,這種模式適用邏輯不復雜且快速返回的情況。

  2. 單線程池模型:
    在IoFilterChain中加入Thread Pool Filter ,當Processor線程讀取數據之後,執行IoFilterChain邏輯,當進行到Thread Pool Filter的時候,此Filter將後續處理封裝至Runnable中,並交給Filter自身的線程池執行,Processor則立即返回處理下一個IO請求。
    如果在IoFilter或者IoHandler中出現了阻塞操作,只會讓執行此操作的Filter阻塞,而不會阻塞負責讀取數據的Processor線程,從而提高服務器的併發及處理能力。
    Mina 提供 Thread Pool Filter 的實現 : ExecutorFilter

  3. 多線程池線程模型
    多線程模型是在上面的兩種模型的基礎上,將單個Thread Pool Filter 變成多個Thread Pool Filter。

請求的處理順序

加入線程池,可以提高服務器的併發吞吐量,但是也存在請求處理順序的問題。

  • 在單線程模型中,多個IO操作是可以按順序依次進行處理的,但加入線程池之後,同一個IoSession的多個請求可能被ExecutorFilter進行並行處理。實際開發中也會有對請求處理有順序要求餓IO請求(如數據庫的順序操作)
  • 在Mina中,默認的實現是保證同一個IoSession中的IO請求的順序的,ExecutorFilter默認採用Mina提供的OrderedThreadPoolExectutor作爲其內置的線程池。線程池從Runnable對象中獲取關聯的IoSession信息,並將其Runnable加入Session的任務列表中,而不是立即執行加入的Runnable對象。線程池的OrderedThreadPoolExectutor
    就會按照session的任務列表處理請求,保證請求的執行順序。
  • 沒有執行順序要求的IO請求,可以爲ExecutorFilter指定一個Executor來替代默認的的OrderedThreadPoolExectutor,實現多個session被並行處理。

Mina編程實現

在這裏插入圖片描述

服務端

初始init()方法

  • 創建acceptor對象
  • 設置解析器IoFilterChain,並設置其讀取數據方式
  • 綁定處理器Handler
  • 綁定端口
  • 創建Acceptor線程專門負責監聽。

Mina服務端的消息處理類
繼承IoHandler有關的類

  • 接收消息處理方法
  • 捕獲異常處理方法
  • 發送信息處理方法
  • 關閉資源處理方法
  • 創建任務處理方法
  • 開啓任務處理方法
客戶端

運行客戶端方法Run()

  • 創建連接線程對象 connector
  • 創建接收數據的過濾器IiFilterChain對象,並設置讀取信息方法
  • 設置服務器的消息處理器
  • 連接到服務器
  • 發送數據

客戶端處理類

繼承IoHandler有關類

  • 構造方法
  • 異常處理方法
  • 接收信息方法
  • 發送信息方法
  • 關閉客戶端 方法
  • 創建客戶端方法
  • 接入客戶端方法

總結

  • Mina服務端的實現

    • 實例化SocketAcceptor
    • 設置解析器
    • 綁定處理器Handler(重寫需要的方法進行相應的邏輯處理)
    • 綁定端口
  • Mina客戶端開發

    • 創建NioSocketConnector對象
    • 設置過濾器和處理器Handler
    • 調用connect方法建立連接
    • 在Handler處理相應的業務邏輯
      可調用IoSession的write方法發送消息
Mina框架總結

服務端接收數據

  1. 服務端啓動,創建Acceptor線程,監聽端口,select connect事件,當獲取新建的連接後將其封裝成IoSession,並交給IoProcessor線程處理。
  2. Processor處理IoSession,維護到一個selector對其Session進行select和遍歷。
  3. IoService 負責Mina底層的IO的相關操作,它調用底層IO接口讀取數據,並封裝成IoBuffer,以事件的形式通知IoFilterChain對數據進行解析處理。對請求的Filter隊列flush操作。
  4. 數據解析後,交給IoHandler去實現業務邏輯。
    服務端發送數據
  • 業務邏輯進行發送操作
  • 發送操作首先通過IoFilterChain到達IoService
  • IoService將發送操作封裝成WriteRequest,放入Session的writeRequestQueue中
  • 交給IoProcessor統一調度flush發送出去。

Netty框架

一款提供異步事件驅動的網絡應用框架,主要用於開發高效,可靠的網絡程序或客戶端程序。它實現了網絡應用開發的簡單化與流線化,如TCP或UDP的Socket開發。吸收了多種協議的經驗,經過精細的設計,形成一套既易開發又保證性能。
在這裏插入圖片描述

零拷貝

  1. Netty的接收和發送採用ByteBuffer,ByteBuffer採用Direct Buffers(即ByteBuffer直接使用堆外內存進行Socket讀寫,而不用進行字節緩衝區的二次拷貝)。相比傳統的使用堆內存(Heap Buffers)的Socket讀寫,JVM再拷貝一份Buffer到內存中,然後再寫入Socket,會少一次緩衝區的內存拷貝。
  2. Netty的組合Buffer對象,能聚合多個ByteBuffer對象,用戶如果要操作多個Buffer,可以先把這些Buffer組合,然後操作這個組合Buffer,而傳統的操作多個Buffer則只能進行內存拷貝成一個大的Buffer,更省內存。
  3. 採用transferTo進行文件傳輸,可以直接把文件緩衝區的數據發送到目標的Channel,從而避免的傳統方式通過循環write導致的內存拷貝問題。
  • NEtty使用自建的Buufer API,來表示一個連續的字節序列
    • Netty新的BUffer類型ChannelBuffer被設計從底層解決ByteBuffer內存問題,可滿足大多數 網絡應用中需要的緩衝類型,並且ByteBuffer允許自定義緩衝類型。
    • ByteBufffer還可以通過內置的緩衝類型實現同透明的零拷貝,並提供開箱即用的動態緩衝類型,容量還可以根據需求進行擴充。
  • Netty的自建ByteBuffer通常比Java NIO 的 ByteBuffer更快。

Codec框架
對應請求的編解碼,Netty中封裝了很多實用的Codec框架。

  • 1,FrameDecoder:
    • 通過維護DynamicChannelBuffer存儲接收的數據,通過抽象的模板(內部已經寫好了整個解碼過程),使用它只需要在子類實現decode方法,
    • 它直接的實現類有DelimiterBasedFrameDecoder(基於分隔符(\r\n)的解碼器,可以在自己的構造方法指定自己的分隔符) 和 LengthFieldBaseFrameDecoder(基於長度字段的解碼器)。
  • 2,ReplayingDecoder
    • ReplayingDecoder是FrameDecoder的非阻塞解碼,如果讀到的數據不完整,使用ReplayingDecoder就可以假設讀到了全部的數據。
  • 3,ObjectEncoder 和 ObjectDecoder
    • 可對java對象進行編解碼序列化
  • 4,HttpRequestEncoder和HttpRequestDecoder
    • Netty中的還能實現 Http服務器,通過HttpRequestEncoder 和HttpRequestDecoder能實現HTTP請求和響應的編解碼。

Channel
Channel在Netty實現的功能:
(1)Channel記錄了當前的狀態信息,即Channel爲打開或關閉。
(2)通過ChannelConfig得到Channel的配置信息。
(3)Channel支持read,write,bind,connect等操作。
(4)通過Channel可以得到處理該Channel的ChannelPipeline。
Netty通過NioServerSocketChannel 封裝ServerSocketChannel, ServerSocketChannel封裝SocketChannel,實現了Java NIO 中的Channel的功能。

ChannelEvent
Netty的最大特色-事件驅動主要通過ChannelEvent實現。通過它,Netty可以確定事件流的方向,ChannelEvent依賴Channel的ChannelPipeline調用Channel Handler進行處理,在ChannelHandler中使用繼承了ChannelEvent的MessageEvent,並調用它的getMessage()方法獲得讀到的ChanelBuffer或者已經被轉化的對象。

ChannelPipeline
Netty控制事件流是通過ChannelPipeline處理,通過調用在ChannelPipeline中註冊的一系列ChannelHandler來處理事件,這其實是很典型的攔截器模式。事件流分爲Upstream和DownStream事件(上行流和下行流)。在ChannelPipeline中,被註冊的ChannelHandler就是ChannelUpstreamHandler或ChannelDownstreamHandler,在ChannelPipeline的傳遞過程中,事件只會調用匹配流的ChannelHandler。

ChannelUpstreamHandler或ChannelDownstreamHandler在事件流的過濾器鏈中既可以終止整個流程,也可以通過調用ChannelHandlerContext.sentUpstream(ChannelEvent)或者ChannelHandlerContext.sentDownstream(ChannelEvent),將當前事件繼續傳遞下去。
Upstream Event 是被UpStreamHandler從下往上依次處理的,而Downstream Event是被DownstreamHandler從上往下依次處理的,這種上下關係實質上就是在初始化的時候,往ChannelPipeline裏添加的Handler的先後順序。也就是說,Upstream Event就是請求外部請求的過程,而Downstream Event用於處理服務器向外發送請求的過程。

Netty編程

  • 服務端實現
    • 創建Netty的服務端,首先需要創建一個boss 線程組和一個work線程組。boss線程組用於接收客戶端的連接,而work線程組用於連接消息的處理,然後創建ServerBootstrap來綁定線程組,綁定ServerChannel(通過NioServerSocektChannel實現),設置option和綁定ChannelHandler之(後,再調用bind綁定端口,完成服務端的創建,ChannelHandler中根據需求重寫父類方法來
  • 服務端的消息處理類
    • channelRead()
    • write()
    • disconnect()
    • channelAcitve()
    • exceptionCaught()
  • 客戶端
    • 通過Bootstrap來綁定ServerChannel,設置option和綁定ChannelHandler之後調用connect方法與服務器建立異步連接。在Netty中通過調用Channel的write方法可發送消息。
  • 客戶端的消息處理類
    • channelRead()
    • write()
    • disconnect()
    • channelActive()
    • exceptiionCaught(

總結

介紹了Java網絡編程相關知識,TCP,UDP,HTTP,Socket,WebSocket等協議的理論,特點,BIO,NIO,AIO等網絡通信模型的原理,最後Mina和Netty網絡通信框架,它們都是同一人設計,只是簡單瞭解主要特徵,現在很多分佈式框架(如阿里Dubbo的底層)基於高效穩定的Netty。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章