成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一篇學(xué)會(huì) Java NIO Channel 的使用

開發(fā) 后端
要從 channel 中讀取數(shù)據(jù)(也就是上面這個(gè)例子),需要先將數(shù)據(jù)讀到 ByteBuffer 中;同理,要想向 channel 中寫入數(shù)據(jù),也需要先將數(shù)據(jù)寫入 ByteBuffer(下面講輸出流的時(shí)候會(huì)講)。

本文轉(zhuǎn)載自微信公眾號(hào)「SH的全棧筆記」,作者SH的全棧筆記。轉(zhuǎn)載本文請(qǐng)聯(lián)系SH的全棧筆記公眾號(hào)。

Java NIO 中的 Channel 分類:

  • FileChannel
  • SocketChannel
  • ServerSocketChannel
  • DatagramChannel

channel 分類

FileChannel: 主要用于文件的讀寫,可以從磁盤上讀取文件,也可以向磁盤上寫入文件。

SocketChannel:用于 Socket 的 TCP 連接的數(shù)據(jù)讀寫,既可以從 Channel 讀數(shù)據(jù),也可以向 Channle 中寫入數(shù)據(jù)

ServerSocketChannel:通過 ServerSocketChannel 可以監(jiān)聽 TCP 連接,服務(wù)端監(jiān)聽到連接之后,會(huì)為每個(gè)請(qǐng)求創(chuàng)建一個(gè) SocketChannel

DatagramChannel:用于 UDP 協(xié)議的數(shù)據(jù)讀寫

接下來就分別介紹一下。

FileChannel

主要用于操作文件,廢話不多說,直接看例子。

準(zhǔn)備文件 test-file.txt ,內(nèi)容 shDEQuanZhanBiJi

test-file.txt 文件

輸入 FileInputStream

用于從 FileChannel 中讀取數(shù)據(jù),例如將指定文件輸入到 FileChannel 中,我們就能獲取到文件的內(nèi)容,接下來編寫 FileChannel 的 輸入流 核心代碼:

  1. public static void main(String[] args) throws IOException { 
  2.   // 創(chuàng)建一個(gè)輸入流 
  3.   FileInputStream fileInputStream = new FileInputStream("test-file.txt"); 
  4.   // 通過輸入流獲取到 channel 
  5.   FileChannel fileChannel = fileInputStream.getChannel(); 
  6.  
  7.   // 準(zhǔn)備好 ByteBuffer 
  8.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  9.   // 將 輸入流 的 channel 的數(shù)據(jù)讀入 buffer 中 
  10.   fileChannel.read(buffer); 
  11.  
  12.   // 簡(jiǎn)單打印 buffer 的內(nèi)容 
  13.   printBuffer(buffer); // shDEQuanZhanBiJi 

這里面的 ByteBuffer 是 channel 進(jìn)行讀、寫數(shù)據(jù)的中間媒介。要從 channel 中讀取數(shù)據(jù)(也就是上面這個(gè)例子),需要先將數(shù)據(jù)讀到 ByteBuffer 中;同理,要想向 channel 中寫入數(shù)據(jù),也需要先將數(shù)據(jù)寫入 ByteBuffer(下面講輸出流的時(shí)候會(huì)講)。

對(duì) ByteBuffer 不熟悉的可以先看看我之前寫的《玩轉(zhuǎn) ByteBuffer》,printBuffer 的代碼里面也有

輸出 FileOutputStream

顧名思義,是 FileChannel 要向外輸出數(shù)據(jù),例如將數(shù)據(jù)寫入到磁盤文件上,接下來通過例子看看效果:

  1. public static void main(String[] args) throws IOException { 
  2.   // 指定需要生成的文件名稱 
  3.   String generateFileName = "generate-file.txt"
  4.   // 創(chuàng)建一個(gè)輸出流 
  5.   FileOutputStream fileOutputStream = new FileOutputStream(generateFileName); 
  6.   // 通過輸出流獲取到 channel 
  7.   FileChannel fileChannel = fileOutputStream.getChannel(); 
  8.  
  9.   // 準(zhǔn)備好 ByteBuffer, 并向里面寫入數(shù)據(jù) 
  10.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  11.   buffer.put("shDEQuanZhanBiJi".getBytes(StandardCharsets.UTF_8)); 
  12.  
  13.   // 將 輸入流 的 channel 的數(shù)據(jù)讀入 buffer 中 
  14.   fileChannel.write(buffer); 
  15.   fileChannel.close(); 

相應(yīng)的注釋都已經(jīng)貼在對(duì)應(yīng)的代碼上了,細(xì)節(jié)在此不再贅述。唯一需要關(guān)注的是,調(diào)用 write 寫文件到磁盤上時(shí),也是先傳入的 ByteBuffer。

好了,當(dāng)你運(yùn)行完代碼你會(huì)發(fā)現(xiàn),雖然文件是生成的了,但是里面卻是空白的...這其實(shí)就涉及到對(duì) ByteBuffer 的熟悉程度了,算是埋的一個(gè)坑。

如果不知道為啥文件是空的,可以去看看上面講 ByteBuffer 的文章,接下來是解答。

這是因?yàn)槲覀儎?chuàng)建一個(gè) ByteBuffer 的時(shí)候默認(rèn)是處于寫模式的,此時(shí)如果去通過 position 和 limit 去讀取數(shù)據(jù)是讀不到的。所以在調(diào)用 write 之前,我們需要先將 ByteBuffer 切換到讀模式,完整代碼如下:

  1. public static void main(String[] args) throws IOException { 
  2.   // 指定需要生成的文件名稱 
  3.   String generateFileName = "generate-file.txt"
  4.   // 創(chuàng)建一個(gè)輸出流 
  5.   FileOutputStream fileOutputStream = new FileOutputStream(generateFileName); 
  6.   // 通過輸出流獲取到 channel 
  7.   FileChannel fileChannel = fileOutputStream.getChannel(); 
  8.  
  9.   // 準(zhǔn)備好 ByteBuffer, 并向里面寫入數(shù)據(jù) 
  10.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  11.   buffer.put("shDEQuanZhanBiJi".getBytes(StandardCharsets.UTF_8)); 
  12.  
  13.   // 將 ByteBuffer 切換到讀模式 
  14.   buffer.flip(); 
  15.   // 將 輸入流 的 channel 的數(shù)據(jù)讀入 buffer 中 
  16.   fileChannel.write(buffer); 
  17.    
  18.   fileChannel.close(); 

可以看到,文件生成了,內(nèi)容也有了:

但是呢,上面將的兩種要么只能寫,要么只能讀。例如 FileInputStream 如果你硬要往 channel 里懟數(shù)據(jù),程序最后會(huì)拋出 NonWritableChannelException 異常,告訴你這玩意兒寫不了。

那有沒有一個(gè)既能寫,又能讀還能唱跳的實(shí)現(xiàn)呢?當(dāng)然有,那就是 RandomAccessFile。

這里提一嘴,調(diào)用完 write 并不是立即就寫入磁盤,也可以在操作系統(tǒng)的緩存里。如果需要立即刷盤,則調(diào)用 channel.force(true); 即可。

RandomAccessFile

怎么用的呢?其實(shí)跟之前兩個(gè)差不多:

  1. public static void main(String[] args) throws IOException { 
  2.   // 指定需要生成的文件名稱 
  3.   String targetFileName = "target-file.txt"
  4.   // 創(chuàng)建 RandomAccessFile, 賦予可讀(r)、可寫(w)的權(quán)限 
  5.   RandomAccessFile accessFile = new RandomAccessFile(targetFileName, "rw"); 
  6.   FileChannel fileChannel = accessFile.getChannel(); 
  7.  
  8.   // 創(chuàng)建 ByteBuffer 并寫入數(shù)據(jù) 
  9.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  10.   buffer.put("shDEQuanZhanBiJi".getBytes(StandardCharsets.UTF_8)); 
  11.   // 切換到 buffer 的讀模式 
  12.   buffer.flip(); 
  13.   // 調(diào)用 write 將 buffer 的數(shù)據(jù)寫入到 channel, channel 再寫數(shù)據(jù)到磁盤文件 
  14.   fileChannel.write(buffer); 
  15.  
  16.   // 相當(dāng)于清空 buffer 
  17.   buffer.clear(); 
  18.   // 將之前寫入到 channel 的數(shù)據(jù)再讀入到 buffer 
  19.   fileChannel.read(buffer); 
  20.  
  21.   // 打印 buffer 中的內(nèi)容 
  22.   printBuffer(buffer); 
  23.  
  24.   fileChannel.close(); 

運(yùn)行之后的效果就是,會(huì)生成一個(gè)名為 target-file.txt 的文件,內(nèi)容就是 shDEQuanZhanBiJi。并且控制臺(tái)會(huì)將之前寫入 channel 的 shDEQuanZhanBiJi 打印出來。

老規(guī)矩,細(xì)節(jié)都在注釋中。值得注意的是 new RandomAccessFile(targetFileName, "rw"); 里的 rw 。注釋里也寫了,代表賦予可讀、可寫的權(quán)限。

再值得注意的是,你不能說把 rw 改成 w。

不能這么玩,因?yàn)樗褪且粋€(gè)單純的字符串匹配,可供選擇的就這么些:

mode 類型

  • 可以看到,r 必不可少...:
  • r 只能讀
  • rw 既能讀,也能寫

rws 和 rwd 功能和 rw 大致是相同的,可讀、可寫。唯一區(qū)別是他們會(huì)將每次改動(dòng)強(qiáng)制刷到磁盤,并且 rws 會(huì)將操作系統(tǒng)對(duì)該文件的元數(shù)據(jù)也一起刷盤,體現(xiàn)就是文件的更新時(shí)間會(huì)更新,而 rwd 不會(huì)將文件的元數(shù)據(jù)刷盤

  1. 兩個(gè) SocketChannel 

由于這倆一個(gè)負(fù)責(zé)連接傳輸,另一個(gè)負(fù)責(zé)連接的監(jiān)聽,所以就放在一起來講了。這一小節(jié)我們大概要做這件事:

客戶端發(fā)送文件到服務(wù)器

但是為了能讓大家直接運(yùn)行起來,客戶端這側(cè)就不從磁盤文件讀取了,直接用 ByteBuffer。大家可以運(yùn)行起來之后,自己嘗試從磁盤上去加載。還是先看代碼,首先是服務(wù)器的:

ServerSocketChannel

  1. public static void main(String[] args) throws IOException { 
  2.   // 打開一個(gè) ServerSocketChannel 
  3.   ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
  4.   // 綁定 8080 端口 
  5.   serverSocketChannel.bind(new InetSocketAddress(8080)); 
  6.  
  7.   // 開始接受客戶端連接 
  8.   SocketChannel socketChannel = serverSocketChannel.accept(); 
  9.   // 獲取連接成功 
  10.   System.out.printf("socketChannel %s connected\n", socketChannel); 
  11.   // 準(zhǔn)備 ByteBuffer 以從 socketChannel 中讀取數(shù)據(jù) 
  12.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  13.  
  14.   // 開始讀取數(shù)據(jù) 
  15.   System.out.println("before read"); 
  16.   int read = socketChannel.read(buffer); 
  17.   System.out.printf("read complete, read bytes length: %s \n"read); 
  18.  
  19.   printBuffer(buffer); 

這里我們使用的是 Java NIO 中默認(rèn)的阻塞模式,僅僅作為一個(gè)掩飾,如果想要 ServerSocketChannel 進(jìn)入非阻塞模式,可在 open 之后,調(diào)用:

  1. serverSocketChannel.configureBlocking(false); 

由于我們這里是阻塞模式,所以在代碼運(yùn)行到 serverSocketChannel.accept(); 時(shí),會(huì)陷入阻塞狀態(tài),直到有客戶端過來建立連接。同理,read 方法也是阻塞的,如果客戶端一直沒有寫入數(shù)據(jù),那么服務(wù)器就會(huì)一直阻塞在 read 。

SocketChannel

直接先給代碼:

  1. public static void main(String[] args) throws IOException { 
  2.   // 打開一個(gè) SocketChannel 
  3.   SocketChannel socketChannel = SocketChannel.open(); 
  4.   // 連接到 localhost 的 8080 端口 
  5.   socketChannel.connect(new InetSocketAddress("localhost", 8080)); 
  6.  
  7.   // 準(zhǔn)備 ByteBuffer 
  8.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  9.   buffer.put(Charset.defaultCharset().encode("test")); 
  10.  
  11.   // 將 buffer 切換成讀模式 & 向 channel 中寫入數(shù)據(jù) 
  12.   buffer.flip(); 
  13.   socketChannel.write(buffer); 

先啟動(dòng)服務(wù)器,再啟動(dòng)客戶端。可以看到服務(wù)器側(cè)的控制臺(tái)有如下的輸出:

  1. socketChannel java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:64373] connected 
  2. before read 
  3. read complete, read bytes length: 4  
  4. BUFFER VALUE: test 

Datagram

這個(gè)就比較簡(jiǎn)單,首先是客戶端的代碼:

  1. public static void main(String[] args) throws IOException { 
  2.   DatagramChannel datagramChannel = DatagramChannel.open(); 
  3.  
  4.   // 構(gòu)建 buffer 數(shù)據(jù) 
  5.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  6.   buffer.put(Charset.defaultCharset().encode("test")); 
  7.  
  8.   // 切換到 buffer 的讀模式 
  9.   buffer.flip(); 
  10.   datagramChannel.send(buffer, new InetSocketAddress("localhost", 8080)); 

然后是服務(wù)器:

  1. public static void main(String[] args) throws IOException { 
  2.   DatagramChannel datagramChannel = DatagramChannel.open(); 
  3.   datagramChannel.bind(new InetSocketAddress(8080)); 
  4.  
  5.   ByteBuffer buffer = ByteBuffer.allocate(16); 
  6.   datagramChannel.receive(buffer); 
  7.  
  8.   printBuffer(buffer); 

 

責(zé)任編輯:武曉燕 來源: SH的全棧筆記
相關(guān)推薦

2022-01-02 08:43:46

Python

2022-02-07 11:01:23

ZooKeeper

2021-12-28 07:20:43

Hippo WebAssembly云原生

2021-11-30 19:58:51

Java問題排查

2022-03-14 08:16:00

Java程序開發(fā)

2022-06-30 22:53:18

數(shù)據(jù)結(jié)構(gòu)算法

2021-08-01 07:19:16

語言OpenrestyNginx

2023-05-08 08:21:15

JavaNIO編程

2021-10-26 10:40:26

代理模式虛擬

2021-12-04 22:05:02

Linux

2022-05-17 08:02:55

GoTryLock模式

2021-05-11 08:54:59

建造者模式設(shè)計(jì)

2021-07-02 09:45:29

MySQL InnoDB數(shù)據(jù)

2021-07-06 08:59:18

抽象工廠模式

2023-01-03 08:31:54

Spring讀取器配置

2021-07-05 22:11:38

MySQL體系架構(gòu)

2023-11-28 08:29:31

Rust內(nèi)存布局

2022-08-23 08:00:59

磁盤性能網(wǎng)絡(luò)

2022-08-26 09:29:01

Kubernetes策略Master

2023-11-29 13:59:00

trait定義接口
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 中文字幕一区二区三区在线观看 | 国产精品视频一二三区 | 国产精品日日摸夜夜添夜夜av | 中文字幕在线第一页 | 日韩电影一区 | 国产乱人伦 | 91精品国产一区二区 | 91国内在线观看 | 搞黄视频免费看 | 亚洲精品一区二区三区免 | 不卡一区二区在线观看 | 国内精品一区二区 | 亚洲精品电影网在线观看 | 在线观看精品 | 亚洲欧洲精品一区 | 国产乱码精品一区二区三区忘忧草 | 久久成人一区 | 玖玖玖在线 | 久草.com| 久久爱综合 | av网站在线看 | a免费在线| 一区二区三区在线 | 九九热精品在线 | 亚洲精品电影在线观看 | 狠狠操狠狠操 | 伊人伊成久久人综合网站 | 麻豆精品久久 | 亚洲国产一区二区三区在线观看 | 久久av.com | 亚洲一区二区三区 | 日本精品久久久久久久 | 国产成人精品一区二区三区四区 | 精品一区二区在线视频 | 午夜午夜精品一区二区三区文 | 精产国产伦理一二三区 | 成人在线观看亚洲 | aaa在线观看| 中文字幕高清一区 | 精品免费视频 | 一区二区三区视频在线观看 |