通信協議
- 客戶端和服務器之間通信所需要遵循的某種規則
網絡模型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
- 客戶端A詢問服務器B: 發送請求建立連接數據包
- 服務器B應答客戶端A:返回同意或者拒絕建立連接的數據包
- 若服務器同意,客戶端則發送建立連接數據包,拒絕則會話結束。
經過上述的三次握手,雙方建立了一條隱形的通信通道,這樣雙方的連接就不會斷開,從而進行收發信號。
- 特點
- 面向連接:通信前要三次”握手“建立連接。
- 安全可靠:每一次通信都必須得到對方的應答,否則認爲數據報丟失,需要重發。
- 全雙工通信:一旦建立連接,雙方都可以通過通道進行數據傳輸。
- 一對一:通信只能建立在兩個點之間
- 面向流通信: 通信傳輸是通過流的形式進行。
2. 面向數據報的UDP
無需建立連接,只要指定目標地址,即可通過UDP向目標地址發送數據報。
由於沒有建立可靠的連接,不保證數據包可以送到目的地,所以數據報可能丟失。
比TCP可以發送更大的數據報,並進行一對多的廣播發送。
- 特點:
- 無連接:通信之前無需建立可靠的連接
- 數據無保障: UDP不對數據排序,數據報文頭部無報文順序信息,而且無需按順序到達,可能造成報文混亂。
- 開銷小:無連接,不保證報文送達和報文順序,開銷較小,而且速度更快。
- 一對一,一對多,多對多:無需連接,可以進行一對一通信,也可以進行一對多的廣播通信和多對多的通信。
UDP和TCP的比較和應用場景
- 大多數情況下,如登錄,支付,上傳等,都需要服務器返回具體的執行結果以及判斷是否成功,需要TCP。
- 在電視直播中,如果每一個幀或者幾個幀畫面都要直播服務器確認,會使得畫面卡頓和一直佔用網絡帶寬,應使用UDP。
3.HTTP編程
超文本傳輸協議,是應用最廣泛的網絡協議,幾乎所有的www 文件都要遵守這個協議的標準。最初是爲了提供一種HTML頁面發佈和接收的方法。
在這裏插入圖片描述
工作原理
- 客戶端請求服務器建立連接併發出請求數據
- 服務器接收請求併發出應答數據,服務器接收到並進行處理,處理之後就返回數據並斷開連接。
拓展知識:
- 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 統一資源名稱
訪問網站的範例
- 客戶端在瀏覽器輸入網址,按回車鍵,瀏覽器封裝URL到HTTP請求體併發送請求包給Web服務器。
- Wub服務器接收請求後建立連接,將請求體中的head和body體中的信息經過服務器邏輯處理後,返回HTML頁面給客戶端。
- 客戶端接收到服務器返回信息結構體,從中讀取HTML代碼,斷開連接。
- 服務器通過瀏覽器解析頁面並呈現在瀏覽器上。
使用Java的net包進行簡單的HTTP請求
- 接收外界傳入的目的網站
- 的網址字符串根據網址創建包含地址信息的URL對象
- 將URL強制類型轉換獲取HttpURLConnection對象
- 然後對對象進行連接信息設置
- 獲取到輸入流,用流對象讀取服務器傳過來的相應內容。
- 在對應答數據執行客戶端的業務邏輯
標誌的HTTP支持六種請求方法:GET,POST,HEAD,PUT,DELETE,OPTIONS,最常用的是GET和POST方法。
GET和POST
- 客戶端
- GET請求默認HttpURLCnnection的設置
- 接收目的服務器的網絡地址字符串
- 構造URL對象
- 設置爲GET請求方式
- 發起連接
- 創建流對象並從通道中讀取服務器的應答信息
- 執行業務邏輯
- POST請求
- 接收目的服務器的網絡地址字符串
- 調用HttpURLConnection的setRequestMethod方法爲POST,並將SetDoOutput爲true,並通過write方法寫入參數到body體。(因爲消息POST消息必須通過body傳遞參數)
- GET請求默認HttpURLCnnection的設置
- 服務端
- 添加一個類,Server繼承servlet,重寫doGet 和 doPost 方法分別接收GET和POST,最後在web端添加此Servert的部署。
4,Socket 編程
-
socket(套接字)用於描述IP地址和端口,是個通信鏈的句柄,可以用來實現不同虛擬機之間或者不同計算機之間的通信。網絡上的主機一般會運行多個服務器,每個服務器上的每一種服務都會打開一個Socket並綁定到一個端口,不同端口對應着不同的服務。
-
IP地址和端口號組成了Socket,Socket 是網絡上運行的程序之間雙向通信鏈路的終結點,是TCP和UDP協議的基礎。
- IP對應着網絡上的計算機,而端口對應着計算機上某個具體的進程或者服務。(就好像寄信一樣,IP地址對應着具體的居民房,端口則對應於人名,根據地址和人名就能實現寄信)
-
根據連接啓動的方式和本地Socket要連接的目標,Socket之間連接分爲三個步驟:
- 服務器監聽、
- 客戶端請求
- 連接確認。
-
服務器監聽
- 服務器端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使用線程池解決這個問題。
線程三種模型
-
單線程模型
Mina默認線程模型,Processor處理了從底層IO到上層IoHander邏輯所執行的所有工作,這種模式適用邏輯不復雜且快速返回的情況。 -
單線程池模型:
在IoFilterChain中加入Thread Pool Filter ,當Processor線程讀取數據之後,執行IoFilterChain邏輯,當進行到Thread Pool Filter的時候,此Filter將後續處理封裝至Runnable中,並交給Filter自身的線程池執行,Processor則立即返回處理下一個IO請求。
如果在IoFilter或者IoHandler中出現了阻塞操作,只會讓執行此操作的Filter阻塞,而不會阻塞負責讀取數據的Processor線程,從而提高服務器的併發及處理能力。
Mina 提供 Thread Pool Filter 的實現 : ExecutorFilter -
多線程池線程模型
多線程模型是在上面的兩種模型的基礎上,將單個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框架總結
服務端接收數據
- 服務端啓動,創建Acceptor線程,監聽端口,select connect事件,當獲取新建的連接後將其封裝成IoSession,並交給IoProcessor線程處理。
- Processor處理IoSession,維護到一個selector對其Session進行select和遍歷。
- IoService 負責Mina底層的IO的相關操作,它調用底層IO接口讀取數據,並封裝成IoBuffer,以事件的形式通知IoFilterChain對數據進行解析處理。對請求的Filter隊列flush操作。
- 數據解析後,交給IoHandler去實現業務邏輯。
服務端發送數據
- 業務邏輯進行發送操作
- 發送操作首先通過IoFilterChain到達IoService
- IoService將發送操作封裝成WriteRequest,放入Session的writeRequestQueue中
- 交給IoProcessor統一調度flush發送出去。
Netty框架
一款提供異步事件驅動的網絡應用框架,主要用於開發高效,可靠的網絡程序或客戶端程序。它實現了網絡應用開發的簡單化與流線化,如TCP或UDP的Socket開發。吸收了多種協議的經驗,經過精細的設計,形成一套既易開發又保證性能。
零拷貝
- Netty的接收和發送採用ByteBuffer,ByteBuffer採用Direct Buffers(即ByteBuffer直接使用堆外內存進行Socket讀寫,而不用進行字節緩衝區的二次拷貝)。相比傳統的使用堆內存(Heap Buffers)的Socket讀寫,JVM再拷貝一份Buffer到內存中,然後再寫入Socket,會少一次緩衝區的內存拷貝。
- Netty的組合Buffer對象,能聚合多個ByteBuffer對象,用戶如果要操作多個Buffer,可以先把這些Buffer組合,然後操作這個組合Buffer,而傳統的操作多個Buffer則只能進行內存拷貝成一個大的Buffer,更省內存。
- 採用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。