1. 程式人生 > >Java網路通訊程式設計基礎

Java網路通訊程式設計基礎

1、Socket

        Socket又稱“套接字”,應用程式通過“套接字”向網路發出請求或者應答網路請求。

        Socket和ServerSocket類庫位於java.net包中。ServerSocket用於伺服器端,Socket是建立網路連線時使用的。在連線成功時,應用程式兩端都會產生一個Socket例項,操作這個例項,完成所需的會話。對於一個網路連線來說,套接字是平等的,不因為在伺服器端或客戶端而產生不同級別。不管是Socket還是ServerSocket,它們的工作都是通過SocketImpl類及其子類完成的。

2、Socket連線過程

        伺服器監聽:是服務端套接字,並不定位具體的客戶端套接字,而是處於等待連線的狀態,實時監控網路狀態。

        客戶端請求伺服器:是指由客戶端的套接字提出連線請求,要連線的目標是伺服器端的套接字。為此,客戶端的套接字必須首先描述它要連線的伺服器的套接字,指出伺服器端套接字的地址和埠號,然後就向伺服器端套接字提出連線請求。

        伺服器端連線確認:當伺服器端套接字監聽或者說接受到客戶端套接字的連線請求,它就響應客戶端套接字的請求,建立一個新的執行緒,把服務端的套接字的描述發給客戶端。

        客戶端連線確認:一旦客戶端確認了此描述,連線就建立好了,雙方開始進行通訊。而伺服器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連線請求。

3、傳統的BIO程式設計

        網路程式設計的基本模型是Client/Server模型,也就是兩個程序直接進行相互通訊,其中服務端提供配置資訊(繫結的IP地址和監聽埠),客戶端通過連線操作向伺服器端監聽的地址發起連線請求,通過三次握手建立連線,如果連線成功,則雙方即可以進行通訊(網路套接字Socket)。(需要為每個連線維護一個執行緒)

4、偽非同步IO

        採用執行緒池和任務佇列可以實現一種偽非同步的IO通訊框架

        將客戶端的socket封裝成一個task任務(實現runnable介面的類),然後投遞到執行緒池中去,配置相應的佇列進行實現。

5、BIO、NIO、AIO的區別

        BIO為同步阻塞形式,NIO為同步非阻塞形式,NIO沒有實現非同步,在JDK1.7之後,升級了NIO庫包,支援非同步非阻塞通訊模型,即NIO2.0(AIO)

6、阻塞與非阻塞

        阻塞概念:應用程式在獲取網路資料的時候,如果網路傳輸速度很慢,那麼程式就一直等著,直到傳輸完畢為止。

        非阻塞概念:應用程式直接可以獲取已經準備就緒的資料,無需等待。

        阻塞說的是具體的技術,接受資料的方式、狀態(IO、NIO)

7、同步與非同步

        同步:應用程式會直接參與IO讀寫操作,並且我們的應用程式會直接阻塞到某一個方法上,直到資料準備就緒;或者採用輪詢的策略實時檢查資料的就緒狀態,如果就緒則獲取資料。

        非同步:所有的IO讀寫操作交給作業系統處理,與我們的應用程式沒有直接關係,我們程式不需要關係IO讀寫,當作業系統完成了IO讀寫操作時,會給我們應用程式傳送通知,我們應用程式直接拿走資料即可。

        同步與非同步的區別:同步和非同步一般是面向作業系統與應用程式對IO操作的層面上來區別的。

8、NIO相關概念

        NIO的本質就是避免原始的TCP建立連線使用3次握手的操作,減少連線的開銷。

        Buffer(緩衝區)、Channel(管道、通道)、Selector(選擇器、多路複用器)

        建立一個Selector相當於一個觀察者開啟一個Server端通道,把這個Server通道註冊到觀察者上並且指定監聽事件,然後遍歷這個觀察者觀察到的事件,取出感興趣的事件再出來。這裡有個最核心的地方就是,我們不需要為每個被觀察者建立一個執行緒來監控它隨時發生的事件,而是把這些被觀察者都註冊一個地方統一管理,再由它把觸發的事件統一發送給感興趣的程式模組。這裡的核心是能夠統一的管理每個被觀察者的事件,所以我們就可以把服務端的每個建立的連線傳送和接受資料作為一個事件統一管理,這樣就不必每個連線需要一個執行緒來維護了。

9、Buffer(緩衝區)

        Buffer是一個物件,它包含一些要寫入或者要讀取的資料。在NIO類庫中加入Buffer物件,體現了現庫與原IO一個重要的區別。在面向流的IO中,可以將資料直接寫入或讀取到Stream物件中。在NIO庫中,所有資料都是用緩衝區處理的(讀寫)。緩衝區實質上也是一個數組,通常它是一個位元組陣列(ByteBuffer),也可以使用其他型別的陣列。這個陣列為緩衝區提供了資料的訪問讀寫等操作屬性,如位置、容量、上限等概念,參考api文件。

        Buffer型別:我們最常用的就是ByteBuffer,實際上每一種java基本型別都對應了一種緩衝區(Boolean型別除外)。(ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer)

10、Channel(通道)

        Channel就像自來水管道一樣,網路資料通過Channel讀取和寫入,通道與流不同之處在於通道是雙向的,而流只是一個方向上移動(一個流必須是InputStream或者OutputStream的子類),而通道可以用於讀、寫或者二者同時進行,最關鍵的是可以與多路複用器結合起來,有多種狀態位,方便多路複用器識別。事實上通道分為兩大類,一類是網路讀寫的(SelectableChannel),一類是用於檔案操作的(FileChannel),我們使用的SocketChannel和ServerSocketChannel都是SelectableChannel的子類。

11、Selector

        多路複用器(Selector),他是NIO程式設計的基礎,非常重要。多路複用器提供選擇已經就緒的任務的能力。

        簡單說,就是Selector會不斷地輪詢註冊在期上的通道(Channel),如果某個通道發生了讀寫操作,這個通道就處於就緒狀態,會被Selector輪詢出來,然後通過SelectionKey可以取得就緒的Channel集合,從而進行後續的IO操作。

        一個多路複用器(Selector)可以負責成千上萬Channel通道,沒有上限。這也是JDK使用了epoll代替了傳統的select實現,獲得連線控制代碼沒有限制。這也就意味著我們只要一個執行緒負責Selector的輪詢,就可以接入成千上萬個客戶端,這是JDK NIO庫巨大的進步。

        Selector執行緒類似一個管理者(Master),管理了成千上萬個通道,然後輪詢哪個管道的資料已經準備好,通知cpu執行IO的讀取或寫入操作。

        Selector模式:當IO事件(管道)註冊到選擇器以後,Selector會分配給每個管道一個key值,相當於標籤。Selector選擇器是以輪詢的方式進行查詢註冊的所有IO事件(管道),當我們的IO事件(管道)準備就緒後,select就會識別,會通過key值來找到相應的管道,進行相關的資料處理操作(從管道里讀或寫資料,寫到我們的快取緩衝區中)。

        每個管道都會對選擇器進行註冊不同的事件狀態,以便選擇查詢。(SelectionKey.OP_CONNECT、SelectionKey.OP_ACCEPT、SelectionKey.OP_READ、SelectionKey.OP_WRITE)

12、AIO

        AIO,在NIP基礎之上引入了非同步通道的概念,並提供了非同步檔案和非同步套接字通道的實現,從而在真正意義上實現了非同步非阻塞,NIO只是非阻塞而並非非同步。而AIO它不需要通過多路複用器對註冊的通道進行輪詢操作即可實現非同步非讀寫,從而簡化了NIO程式設計模型。也可以稱之為NIO2.0,這種模式才真正的屬於我們非同步非阻塞的模型。(AsynchronusServerSocketChannel、AsynchronousSocketChannel)

13、Netty簡介

        Netty是基於Java NIO的網路應用框架。Netty是一個NIO client-server(客戶端伺服器)框架,使用Netty可以快速開發網路應用,例如伺服器和客戶端協議。Netty提供了一種新的方式來開發網路應用程式,這種新的方式使得它很容易使用和很強的擴充套件性。Netty的內部實現是很複雜的,但是Netty提供了簡單易用的api從網路處理程式碼中解耦業務邏輯。Netty是完全基於NIO實現的,所以整個Netty都是非同步的。

        網路應用程式通常需要較高的可擴充套件性,無論是Netty還是其他的基於Java NIO的框架,都會提供可擴充套件性的解決方案。Netty中一個關鍵組成部分是它的非同步特性。

        Netty是最流行的NIO框架,他的健壯性、功能、效能、可定製性和可擴充套件性在同類框架裡都是首屈一指的。它已經得到成百上千的商業/商用專案驗證。

14、Netty實現通訊的步驟

        建立兩個NIO執行緒組,(IO多路複用模型),一個專門用於網路事件處理(接受客戶端的連線),另一個則進行網路通訊讀寫。

        建立一個ServerBootstrap物件,配置Netty的一系列引數,例如接受傳出資料的快取大小等等。

        建立一個實際處理資料的類ChannelInitializer,進行初始化的準備工作,比如設定接受傳出資料的字符集、格式、以及實際處理資料的介面。

        繫結埠,執行同步阻塞方法等待伺服器啟動即可。

15、TCP粘包、拆包問題

        無論是伺服器端還是客戶端,當我們讀取或者傳送資料的時候,都需要考慮TCP底層的粘包/拆包機制。

        TCP是一個“流”協議,所謂流就是沒有界限的遺傳資料。可以想象下如果河裡的水就好比資料,它們是連成一片的,沒有分界線,TCP底層並不瞭解上層業務的具體的含義,他會根據TCP緩衝區的實際情況進行包的劃分,也就是說,在業務上,我們一個完整的包可能會被TCP分成多個包進行傳送,也可能把多個小包封裝成一個大的資料包傳送出來,這就是所謂的TCP粘包、拆包問題。

16、TCP粘包、拆包問題的產生原因

        應用程式write寫入的位元組大小大於套介面傳送緩衝區的大小

        進行MSS大小的TCP分段

        乙太網幀的payload大於MTU進行IP分片

17、拆包、粘包問題的解決方案

        訊息定長,例如每個報文的大小固定為200個位元組,如果不夠空位補空格;

        在包尾部增加特殊字元進行分割。例如加回車等級。

        講訊息分為訊息頭和訊息體,在訊息頭中包含表示訊息總長度的欄位,然後進行業務邏輯的處理。

18、Netty編解碼技術

        編解碼技術,說白了就是java序列化技術,序列化的目的就兩個,第一進行網路傳輸,第二持久化。

        雖然可以使用java進行物件序列化,netty去傳輸,但是java序列化的硬傷太多,比如java序列化沒法跨語言、序列化後碼流太大、序列化效能太低等等

        主流的編解碼框架:JBoss的Marshaling包,google的Protobuf、基於Protobuf的Kyro、MessagePack框架

19、資料通訊

        第一種:使用長連線通道不斷開的形式進行通訊,也就是伺服器和客戶端的通道一直處於開啟狀態,如果伺服器效能足夠好,並且客戶端數量也比較少的情況下,還是推薦這種的。

        第二種:一次性批量提交資料,採用短連線方式。也就是我們會把資料儲存在本地臨時緩衝區或者臨時表裡,當達到臨界值時進行一次批量提交,又或者根據定時任務查詢提交,這種情況弊端是做不到實時性傳輸,在對實時性不高的應用程式中可以推薦使用。

        第三種:我們可以用一種特殊的長連線,在指定某一段時間內,伺服器與某臺客戶端沒有通訊,則斷開連線。下次連線則是客戶端向伺服器傳送請求的時候,再次建立連線,但是這種模式我們需要考慮兩個因素:①如何在超時(即伺服器與客戶端沒有任何通訊)後關閉通道?關閉通道後我們又如何再次建立連線?②客戶端宕機時,我們無需考慮,下次客戶端重啟之後就可以與伺服器建立連線,但是伺服器宕機時,我們的客戶端如何與伺服器進行連線呢?

20、心跳監控

        使用Socket通訊一般經常會處理多個伺服器之間的心跳檢測。一般來講我們去維護伺服器叢集,肯定要有一臺或幾臺伺服器主機(Master),然後還應該有N臺(Slave),那麼我們的主機肯定要時時刻刻知道自己下面的從伺服器的各方面情況,然後進行實時監控的功能,這個分散式架構裡叫心跳監控。處理方案可以使用一些通訊框架進行實現,Netty就可以去做這樣一件事。

        可以用sigar jar包獲取機器狀態資訊

21、HTTP協議

        HTTP(超文字傳輸協議)是建立在TCP傳輸協議之上的應用層協議,它目前主流是針對於web開發,HTTP協議應用非常廣泛,因此掌握HTTP協議的開發非常重要。使用Netty的Http協議也是非同步非阻塞的。

22、HTTP協議特點

        簡單:客戶端請求伺服器時只要指定UPL和攜帶必要的引數即可。

        靈活:HTTP協議允許傳輸任意型別的資料物件,傳輸內容HTTP頭中的Content-Type加以標記。

        無狀態:HTTP協議是無狀態的,無狀態指的是協議對事物處理沒有記憶能力。這意味著如果

後續處理需要之前的資訊,則它必須重新獲取。也從側面體系HTTP協議的設計是為了使網路傳輸更加的輕量級、敏捷、負載較輕。

23、HTTP協議組成部分

        請求行、請求頭、請求正文(實體內容)

24、Netty HTTP檔案服務端開發

        Netty的HTTP協議棧無論從效能上還是可靠性上都表現優異,非常適合web場景下運用,相比於傳統的Tomcat、Jetty等Web容器,他更加的輕量和小巧,靈活性和定製性也更好