天天熬夜敲代碼 2021-09-19 01:23:44 阅读数:881
Socket
本身有“插座”的意思,不是Java中特有的概念,而是一個語言無關的標准,任何可以實現網絡編程的編程語言都有Socket
。在Linux
環境下,用於錶示進程間網絡通信的特殊文件類型,其本質為內核借助緩沖區形成的偽文件。既然是文件,那麼理所當然的,我們可以使用文件描述符引用套接字。
與管道類似的,Linux
系統將其封裝成文件的目的是為了統一接口,使得讀寫套接字和讀寫文件的操作一致。區別是管道主要應用於本地進程間通信,而套接字多應用於網絡進程間數據的傳遞。
可以這麼理解:Socket
就是網絡上的兩個應用程序通過一個雙向通信連接實現數據交換的編程接口API。
Socket
通信的基本流程具體步驟如下所示:
(1)服務端通過Listen
開啟監聽,等待客戶端接入。
(2)客戶端的套接字通過Connect
連接服務器端的套接字,服務端通過Accept
接收客戶端連接。在connect-accept
過程中,操作系統將會進行三次握手。
(3)客戶端和服務端通過write
和read
發送和接收數據,操作系統將會完成TCP
數據的確認、重發等步驟。
(4)通過close
關閉連接,操作系統會進行四次揮手。
針對Java編程語言,java.net
包是網絡編程的基礎類庫。其中ServerSocket
和Socket
是網絡編程的基礎類型。
SeverSocket
是服務端應用類型。Socket
是建立連接的類型。當連接建立成功後,服務器和客戶端都會有一個Socket
對象示例,可以通過這個Socket
對象示例,完成會話的所有操作。對於一個完整的網絡連接來說,Socket
是平等的,沒有服務器客戶端分級情况。
對於一次IO操作,數據會先拷貝到內核空間中,然後再從內核空間拷貝到用戶空間中,所以一次read
操作,會經曆兩個階段:
(1)等待數據准備
(2)數據從內核空間拷貝到用戶空間
基於以上兩個階段就產生了五種不同的IO模式。
這五種IO模式不難發現存在這兩對關系:同步和异步、阻塞和非阻塞。那麼稍微解釋一下:
同步和异步的區別最大在於异步的話調用者不需要等待處理結果,被調用者會通過回調等機制來通知調用者其返回結果。
阻塞和非阻塞是針對進程在訪問數據的時候,根據IO操作的就緒狀態來采取的不同方式,說白了是一種讀取或者寫入操作方法的實現方式,阻塞方式下讀取或者寫入函數將一直等待,而非阻塞方式下,讀取或者寫入方法會立即返回一個狀態值。
如果組合後的同步阻塞(blocking-IO
)簡稱BIO
、同步非阻塞(non-blocking-IO
)簡稱NIO
和异步非阻塞(asynchronous-non-blocking-IO
)簡稱AIO
又代錶什麼意思呢?
java
中的 BIO
、NIO
和AIO
理解為是 Java 語言
在操作系統層面對這三種 IO
模型的封裝。程序員在使用這些 封裝API 的時候,不需要關心操作系統層面的知識,也不需要根據不同操作系統編寫不同的代碼,只需要使用Java
的API就可以了。由此,為了使讀者對這三種模型有個比較具體和遞推式的了解,並且和本文主題NIO
有個清晰的對比,下面繼續延伸。
BIO
編程方式通常是是Java的上古產品,自JDK 1.0-JDK1.4就有的東西。編程實現過程為:首先在服務端啟動一個ServerSocket
來監聽網絡請求,客戶端啟動Socket
發起網絡請求,默認情况下SeverSocket
會建立一個線程來處理此請求,如果服務端沒有線程可用,客戶端則會阻塞等待或遭到拒絕。服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理。大致結構如下:
如果要讓 BIO
通信模型能够同時處理多個客戶端請求,就必須使用多線程(主要原因是 socket.accept()
、socket.read()
、 socket.write()
涉及的三個主要函數都是同步阻塞的),也就是說它在接收到客戶端連接請求之後為每個客戶端創建一個新的線程進行鏈路處理,處理完成之後,通過輸出流返回應答給客戶端,線程銷毀。這就是典型的 一請求一應答通信模型 。我們可以設想一下如果這個連接不做任何事情的話就會造成不必要的線程開銷,不過可以通過線程池機制改善,線程池還可以讓線程的創建和回收成本相對較低。使用線程池機制改善後的 BIO
模型圖如下:
BIO
方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,並發局限於應用中,是JDK1.4以前的唯一選擇,但程序直觀簡單易懂。Java BIO
編程示例網上很多,這裏就不進行coding舉例了,畢竟後面NIO
才是重點。
NIO
(New IO或者No-Blocking IO),從JDK1.4 開始引入的非阻塞IO
,是一種非阻塞
+ 同步
的通信模式。這裏的No Blocking IO
用於區分上面的BIO
。
NIO
本身想解决 BIO
的並發問題,通過Reactor模式
的事件驅動機制來達到Non Blocking
的。當 socket
有流可讀或可寫入 socket
時,操作系統會相應的通知應用程序進行處理,應用再將流讀取到緩沖區或寫入操作系統。
也就是說,這個時候,已經不是一個連接就 要對應一個處理線程了,而是有效的請求,對應一個線程,當連接沒有數據時,是沒有工作線程來處理的。
當一個連接創建後,不需要對應一個線程,這個連接會被注册到 多路複用器
上面,所以所有的連接只需要一個線程就可以搞定,當這個線程中的多路複用器
進行輪詢的時候,發現連接上有請求的話,才開啟一個線程進行處理,也就是一個請求一個線程模式。
NIO
提供了與傳統BIO模型中的Socket
和ServerSocket
相對應的SocketChannel
和ServerSocketChannel
兩種不同的套接字通道實現,如下圖結構所示。這裏涉及的Reactor
設計模式、多路複用Selector
、Buffer
等暫時不用管,後面會講到。
NIO 方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,並發局 限於應用中,編程複雜,JDK1.4 開始支持。同時,NIO
和普通IO的區別主要可以從存儲數據的載體、是否阻塞等來區分:
與 NIO
不同,當進行讀寫操作時,只須直接調用 API 的 read
或 write
方法即可。這兩種方法均為异步的,對於讀操作而言,當有流可讀取時,操作系統會將可讀的流傳入 read
方 法的緩沖區,並通知應用程序;對於寫操作而言,當操作系統將 write
方法傳遞的流寫入完畢時,操作系統主動通知應用程序。即可以理解為,read/write
方法都是异步的,完成後會主動調用回調函數。在 JDK7
中,提供了异步文件通道和异步套接字通道的實現,這部分內容被稱作 NIO
.
AIO
方式使用於連接數目多且連接比較長(重操作)的架構,比如相册服務器,充分調用 OS
參與並發操作,編程比較複雜,JDK7
開始支持。
目前來說 AIO
的應用還不是很廣泛,Netty
之前也嘗試使用過 AIO
,不過又放弃了。
在NIO
中,基本所有的IO操作都是從Channel
開始的,Channel
通過Buffer(緩沖區)
進行讀寫操作。
read()
錶示讀取通道中數據到緩沖區,write()
錶示把緩沖區數據寫入到通道。
Channel
有好多實現類,這裏有三個最常用:
SocketChannel
:一個客戶端發起TCP連接的ChannelServerSocketChannel
:一個服務端監聽新連接的TCP Channel,對於每一個新的Client連接,都會建立一個對應的SocketChannelFileChannel
:從文件中讀寫數據其中SocketChannel
和ServerSocketChannel
是網絡編程中最常用的,一會在最後的示例代碼中會有講解到具體用法。
Buffer
也被成為內存緩沖區,本質上就是內存中的一塊,我們可以將數據寫入這塊內存,之後從這塊內存中讀取數據。也可以將這塊內存封裝成NIO Buffer
對象,並提供一組常用的方法,方便我們對該塊內存進行讀寫操作。
Buffer
在java.nio
中被定義為抽象類:
我們可以將Buffer
理解為一個數組的封裝,我們最常用的ByteBuffer
對應的數據結構就是byte[]
Buffer
中有4個非常重要的屬性:capacity、limit、position、mark
CodeChina開源項目:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視頻】
版权声明:本文为[天天熬夜敲代碼]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210919012344146Y.html