自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一篇文章讀不懂:IO vs. NIO

存儲(chǔ) 存儲(chǔ)軟件
處理輸入和輸出是Java程序員的常見任務(wù),本教程中,我們將介紹 原始的 java.io (IO) 庫(kù)和較新的 java.nio (NIO) 庫(kù) 以及它們?cè)谕ㄟ^網(wǎng)絡(luò)進(jìn)行通信時(shí)的區(qū)別。.

[[320131]]

1. 概覽

處理輸入和輸出是Java程序員的常見任務(wù),本教程中,我們將介紹 原始的 java.io (IO) 庫(kù)和較新的 java.nio (NIO) 庫(kù) 以及它們?cè)谕ㄟ^網(wǎng)絡(luò)進(jìn)行通信時(shí)的區(qū)別。.

2. 關(guān)鍵特性

讓我們先來(lái)看看這兩個(gè)包的關(guān)鍵特性。

2.1. IO – java.io

java.io 包是在Java 1.0引入的,而Reader 則是在 Java 1.1中引入。它提供:

  • InputStream 和 OutputStream – 一次提供一個(gè)字節(jié)的數(shù)據(jù)。
  • Reader 和 Writer – 包裝流
  • 阻塞模式(blocking mode) – 等待完整的消息

2.2. NIO – java.nio

java.nio 包在Java 1.4中被引入 并在 Java 1.7 (NIO.2) 更新了,其中包含 增強(qiáng)的文件操作 和 ASynchronousSocketChannel。它提供 :

  • Buffer – 一個(gè)讀取數(shù)據(jù)塊
  • CharsetDecoder – 用于將原始字節(jié)映射到可讀字符/從可讀字符映射原始字節(jié)
  • Channel – 與外界溝通
  • Selector – 在 SelectableChannel 上啟用多路復(fù)用,并提供對(duì)任何準(zhǔn)備好進(jìn)行I/O的 Channels 的訪問
  • 非阻塞模式(non-blocking mode) – 讀取任何準(zhǔn)備好的東西

現(xiàn)在,讓我們看看在向服務(wù)器發(fā)送數(shù)據(jù)或讀取其響應(yīng)時(shí)如何使用這些包。

3. 配置測(cè)試服務(wù)器

在這里,我們將使用 WireMock 來(lái)模擬另一臺(tái)服務(wù)器,以便我們可以獨(dú)立運(yùn)行測(cè)試。

配置這臺(tái)服務(wù)器來(lái)監(jiān)聽請(qǐng)求,并像真正的web服務(wù)器一樣向我們發(fā)送響應(yīng)。同時(shí)我們還將使用動(dòng)態(tài)端口,這樣就不會(huì)與本地計(jì)算機(jī)上的任何服務(wù)沖突。

讓我們添加WireMock Maven依賴項(xiàng)到 test scope:

Let's add the Maven dependency for WireMock with test scope:

  1. <dependency> 
  2.     <groupId>com.github.tomakehurst</groupId> 
  3.     <artifactId>wiremock-jre8</artifactId> 
  4.     <version>2.26.3</version> 
  5.     <scope>test</scope> 
  6. </dependency> 

 

在測(cè)試類中,讓我們定義一個(gè) JUnit*@Rule* 來(lái)在空閑端口上啟動(dòng) WireMock 。然后,我們將對(duì)其進(jìn)行配置,使其在要求預(yù)定義資源時(shí)返回一個(gè) HTTP 200 響應(yīng),消息體為 JSON 格式的文本:

  1. @Rule public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort()); 
  2.   
  3. private String REQUESTED_RESOURCE = "/test.json"
  4.   
  5. @Before 
  6. public void setup() { 
  7.     stubFor(get(urlEqualTo(REQUESTED_RESOURCE)) 
  8.       .willReturn(aResponse() 
  9.       .withStatus(200) 
  10.       .withBody("{ \"response\" : \"It worked!\" }"))); 

現(xiàn)在已經(jīng)建立了模擬服務(wù)器,我們準(zhǔn)備運(yùn)行一些測(cè)試。

4. Blocking IO – java.io

我們可通過從網(wǎng)站上讀取一些數(shù)據(jù)來(lái)了解原始的阻塞IO模型是如何工作的,例如:使用一個(gè) java.net.Socket 來(lái)訪問操作系統(tǒng)的一個(gè)端口。

4.1. 發(fā)送請(qǐng)求(Request)

在這個(gè)例子中,我們將創(chuàng)建一個(gè)GET請(qǐng)求來(lái)檢索資源。首先,創(chuàng)建一個(gè) Socket 來(lái)訪問我們的WireMock服務(wù)器正在監(jiān)聽的端口:

  1. Socket socket = new Socket("localhost", wireMockRule.port() 

對(duì)于普通的 HTTP 或 HTTPS 通信,端口應(yīng)該是 80 或 443 。但是,在本例中,我們使用wireMockRule.port() 來(lái)訪問前面設(shè)置的動(dòng)態(tài)端口?,F(xiàn)在,我們?cè)谔捉幼稚洗蜷_一個(gè) OutputStream ,包裝在 OutputStreamWriter 中,并將其傳遞給 PrintWiter 來(lái)編寫我們的消息。確保刷新緩沖區(qū)以便發(fā)送我們的請(qǐng)求:

  1. OutputStream clientOutput = socket.getOutputStream(); 
  2. PrintWriter writer = new PrintWriter(new OutputStreamWriter(clientOutput)); 
  3. writer.print("GET " + TEST_JSON + " HTTP/1.0\r\n\r\n"); 
  4. writer.flush(); 

4.2. 等待響應(yīng)(Response)

打開套接字上的 InputStream 來(lái)獲取響應(yīng),使用 BufferedReader 讀取流,并將其存儲(chǔ)在 StringBuilder 中:

  1. InputStream serverInput = socket.getInputStream(); 
  2. BufferedReader reader = new BufferedReader(new InputStreamReader(serverInput)); 
  3. StringBuilder ourStore = new StringBuilder(); 

我們使用 reader.readLine() 來(lái)阻塞,等待一個(gè)完整的行,然后將該行追加到我們的存儲(chǔ)中。我們將一直讀取,直到得到一個(gè)空值,它指示流的結(jié)尾:

  1. for (String line; (line = reader.readLine()) != null;) { 
  2.    ourStore.append(line); 
  3.    ourStore.append(System.lineSeparator()); 

5. Non-Blocking IO – java.nio

現(xiàn)在,讓我們看看 NIO包 的非阻塞IO模型是如何與同一個(gè)例子一起工作的。

這次,我們將創(chuàng)建一個(gè) java.nio.channel.SocketChannel 來(lái)訪問服務(wù)器上的端口,而不是java.net.Socket,并向它傳遞一個(gè)InetSocketAddress。

5.1. 發(fā)送 Request

首先, 打開 SocketChannel:

  1. InetSocketAddress address = new InetSocketAddress("localhost", wireMockRule.port()); 
  2. SocketChannel socketChannel = SocketChannel.open(address); 

現(xiàn)在,讓我們使用一個(gè)標(biāo)準(zhǔn)的UTF-8字符集 來(lái)編碼和編寫我們的消息:

  1. Charset charset = StandardCharsets.UTF_8; 
  2. socket.write(charset.encode(CharBuffer.wrap("GET " + REQUESTED_RESOURCE + " HTTP/1.0\r\n\r\n"))); 

5.2. 讀取 Response

發(fā)送請(qǐng)求后,我們可以使用原始緩沖區(qū)以非阻塞模式讀取響應(yīng)。

既然要處理文本,那么我們需要一個(gè) ByteBuffer 來(lái)處理原始字節(jié),一個(gè)CharBuffer 用來(lái)轉(zhuǎn)換字符(借助 CharsetDecoder):

  1. ByteBuffer byteBuffer = ByteBuffer.allocate(8192); 
  2. CharsetDecoder charsetDecoder = charset.newDecoder(); 
  3. CharBuffer charBuffer = CharBuffer.allocate(8192); 

如果數(shù)據(jù)是以多字節(jié)字符集發(fā)送的,CharBuffer 將有剩余空間。

注意,如果需要特別快的性能,我們可以使用 ByteBuffer.allocateDirect() 在本機(jī)內(nèi)存中創(chuàng)建一個(gè)MappedByteBuffer。然而,在我們的例子中,從標(biāo)準(zhǔn)堆中使用 allocate() 已經(jīng)足夠快了。

在處理緩沖區(qū)時(shí),我們需要知道緩沖區(qū)有多大(capacity),我們?cè)诰彌_區(qū)中的位置(current position),以及我們能走多遠(yuǎn)(limit)。

所以,我們從SocketChannel中讀取,將它傳遞給 ByteBuffer 來(lái)存儲(chǔ)我們的數(shù)據(jù)。從 SocketChannel讀取將以 ByteBuffer的當(dāng)前位置為下一個(gè)要寫入的字節(jié)(就在寫入最后一個(gè)字節(jié)之后)結(jié)束,但其限制(limit)不變:

  1. socketChannel.read(byteBuffer) 

Our SocketChannel.read() 返回可以寫入緩沖區(qū)的讀取字節(jié)數(shù) ,如果斷開連接,則會(huì)變成 -1.

當(dāng)緩沖區(qū)由于尚未處理其所有數(shù)據(jù)而沒有剩余空間時(shí),SocketChannel.read() 將返回讀取的零字節(jié),但buffer.position() 仍將大于零。

確保從緩沖區(qū)的正確位置開始讀取, 我們將使用 Buffer.flip() 來(lái)設(shè)置 ByteBuffer 的當(dāng)前位置為0 以及它對(duì) SocketChannel 寫入的最后一個(gè)字節(jié)的限制。然后,我們將使用 storeBufferContents 方法保存緩沖區(qū)內(nèi)容,稍后我們將查看該方法。最后,使用 buffer.compact() 壓縮緩沖區(qū)并設(shè)置當(dāng)前位置,以便下次從 SocketChannel 讀取。

由于數(shù)據(jù)可能部分到達(dá),需要用終止條件將緩沖區(qū)讀取代碼包裝成一個(gè)循環(huán),以檢查套接字是否仍然連接,或者是否已斷開連接,但緩沖區(qū)中仍有數(shù)據(jù):

  1. while (socketChannel.read(byteBuffer) != -1 || byteBuffer.position() > 0) { 
  2.     byteBuffer.flip(); 
  3.     storeBufferContents(byteBuffer, charBuffer, charsetDecoder, ourStore); 
  4.     byteBuffer.compact(); 

別忘了關(guān)閉套接字(除非我們?cè)趖ry with resources塊中打開它):

  1. socketChannel.close(); 

5.3. Buffer存儲(chǔ)數(shù)據(jù)

來(lái)自服務(wù)器的響應(yīng)將包含頭,這可能會(huì)使數(shù)據(jù)量超過緩沖區(qū)的大小。因此,我們將使用StringBuilder在消息到達(dá)時(shí)構(gòu)建完整的消息。為了存儲(chǔ)我們的消息,我們首先將原始字節(jié)解碼為我們的 CharBuffer 中的字符。然后翻轉(zhuǎn)指針,以便讀取字符數(shù)據(jù),并將其附加到可擴(kuò)展的 StringBuilder. 最后,清除CharBuffer以準(zhǔn)備下一個(gè)寫/讀循環(huán)。現(xiàn)在,讓我們實(shí)現(xiàn)傳入緩沖區(qū)的完整 storeBufferContents() 方法,CharsetDecoder 和 StringBuilder:

  1. void storeBufferContents(ByteBuffer byteBuffer, CharBuffer charBuffer,  
  2.   CharsetDecoder charsetDecoder, StringBuilder ourStore) { 
  3.     charsetDecoder.decode(byteBuffer, charBuffer, true); 
  4.     charBuffer.flip(); 
  5.     ourStore.append(charBuffer); 
  6.     charBuffer.clear(); 

6. 總結(jié)

本文中, 我們已經(jīng)看到原始java.io模型如何阻塞,等待請(qǐng)求,并使用 Streams 來(lái)操作它接收到的數(shù)據(jù)。相反,java.nio庫(kù)允許使用Buffers和Channels進(jìn)行非阻塞通信,并且可以提供直接內(nèi)存訪問以獲得更快的性能。然而,這種速度帶來(lái)了處理緩沖區(qū)的額外復(fù)雜性。

在本文中,我們看到了原始 java.io 模型如何阻塞,如何等待請(qǐng)求并使用Streams來(lái)處理它接收到的數(shù)據(jù)。相反,java.nio庫(kù)允許使用Buffers和Channels進(jìn)行非阻塞通信,并且可以提供直接內(nèi)存訪問以獲得更快的性能。然而,這種速度帶來(lái)了處理緩沖區(qū)的額外復(fù)雜性。

一如既往, 代碼 over on GitHub.

 

責(zé)任編輯:武曉燕 來(lái)源: 鍋外的大佬
相關(guān)推薦

2023-05-08 08:21:15

JavaNIO編程

2020-10-09 08:15:11

JsBridge

2020-10-23 07:56:04

Java中的IO流

2017-09-05 08:52:37

Git程序員命令

2022-02-21 09:44:45

Git開源分布式

2023-05-12 08:19:12

Netty程序框架

2021-06-30 00:20:12

Hangfire.NET平臺(tái)

2019-04-17 15:16:00

Sparkshuffle算法

2024-06-25 08:18:55

2021-04-09 08:40:51

網(wǎng)絡(luò)保險(xiǎn)網(wǎng)絡(luò)安全網(wǎng)絡(luò)風(fēng)險(xiǎn)

2022-02-23 09:36:11

GoRuby編程語(yǔ)言

2022-02-17 08:35:59

OLTPOLAP數(shù)據(jù)倉(cāng)庫(kù)

2020-02-27 21:24:31

JavaAIOBIO

2023-07-30 15:18:54

JavaScript屬性

2021-01-26 23:46:32

JavaScript數(shù)據(jù)結(jié)構(gòu)前端

2021-03-09 14:04:01

JavaScriptCookie數(shù)據(jù)

2021-06-24 09:05:08

JavaScript日期前端

2021-09-27 09:18:30

ListIterato接口方法

2023-09-06 14:57:46

JavaScript編程語(yǔ)言

2024-01-30 13:47:45

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)