<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Java NIO 中的 Channel 分類:
FileChannel: 主要用於檔案的讀寫,可以從磁碟上讀取檔案,也可以向磁碟上寫入檔案。
SocketChannel:用於 Socket 的 TCP 連線的資料讀寫,既可以從 Channel 讀資料,也可以向 Channle 中寫入資料
ServerSocketChannel:通過 ServerSocketChannel 可以監聽 TCP 連線,伺服器端監聽到連線之後,會為每個請求建立一個 SocketChannel
DatagramChannel:用於 UDP 協定的資料讀寫
接下來就分別介紹一下。
主要用於操作檔案,廢話不多說,直接看例子。
準備檔案
test-file.txt
,內容shDEQuanZhanBiJi
用於從 FileChannel 中讀取資料,例如將指定檔案輸入到 FileChannel 中,我們就能獲取到檔案的內容,接下來編寫 FileChannel 的 輸入流 核心程式碼:
public static void main(String[] args) throws IOException { // 建立一個輸入流 FileInputStream fileInputStream = new FileInputStream("test-file.txt"); // 通過輸入流獲取到 channel FileChannel fileChannel = fileInputStream.getChannel(); // 準備好 ByteBuffer ByteBuffer buffer = ByteBuffer.allocate(16); // 將 輸入流 的 channel 的資料讀入 buffer 中 fileChannel.read(buffer); // 簡單列印 buffer 的內容 printBuffer(buffer); // shDEQuanZhanBiJi }
這裡面的 ByteBuffer 是 channel 進行讀、寫資料的中間媒介。要從 channel 中讀取資料(也就是上面這個例子),需要先將資料讀到 ByteBuffer 中;同理,要想向 channel 中寫入資料,也需要先將資料寫入 ByteBuffer(下面講輸出流的時候會講)。
對 ByteBuffer 不熟悉的可以先看看我之前寫的 《玩轉 ByteBuffer》,
printBuffer
的程式碼裡面也有
顧名思義,是 FileChannel 要向外輸出資料,例如將資料寫入到磁碟檔案上,接下來通過例子看看效果:
public static void main(String[] args) throws IOException { // 指定需要生成的檔名稱 String generateFileName = "generate-file.txt"; // 建立一個輸出流 FileOutputStream fileOutputStream = new FileOutputStream(generateFileName); // 通過輸出流獲取到 channel FileChannel fileChannel = fileOutputStream.getChannel(); // 準備好 ByteBuffer, 並向裡面寫入資料 ByteBuffer buffer = ByteBuffer.allocate(16); buffer.put("shDEQuanZhanBiJi".getBytes(StandardCharsets.UTF_8)); // 將 輸入流 的 channel 的資料讀入 buffer 中 fileChannel.write(buffer); fileChannel.close(); }
相應的註釋都已經貼在對應的程式碼上了,細節在此不再贅述。唯一需要關注的是,呼叫 write
寫檔案到磁碟上時,也是先傳入的 ByteBuffer。
好了,當你執行完程式碼你會發現,雖然檔案是生成的了,但是裡面卻是空白的…這其實就涉及到對 ByteBuffer 的熟悉程度了,算是埋的一個坑。
如果不知道為啥檔案是空的,可以去看看上面講 ByteBuffer 的文章,接下來是解答。
這是因為我們建立一個 ByteBuffer 的時候預設是處於寫模式的,此時如果去通過 position 和 limit 去讀取資料是讀不到的。所以在呼叫 write
之前,我們需要先將 ByteBuffer 切換到讀模式,
完整程式碼如下:
public static void main(String[] args) throws IOException { // 指定需要生成的檔名稱 String generateFileName = "generate-file.txt"; // 建立一個輸出流 FileOutputStream fileOutputStream = new FileOutputStream(generateFileName); // 通過輸出流獲取到 channel FileChannel fileChannel = fileOutputStream.getChannel(); // 準備好 ByteBuffer, 並向裡面寫入資料 ByteBuffer buffer = ByteBuffer.allocate(16); buffer.put("shDEQuanZhanBiJi".getBytes(StandardCharsets.UTF_8)); // 將 ByteBuffer 切換到讀模式 buffer.flip(); // 將 輸入流 的 channel 的資料讀入 buffer 中 fileChannel.write(buffer); fileChannel.close(); }
可以看到,檔案生成了,內容也有了:
但是呢,上面將的兩種要麼只能寫,要麼只能讀。例如 FileInputStream
如果你硬要往 channel 裡懟資料,程式最後會丟擲 NonWritableChannelException
異常,告訴你這玩意兒寫不了。
那有沒有一個既能寫,又能讀還能唱跳的實現呢?當然有,那就是 RandomAccessFile
。
這裡提一嘴,呼叫完 write 並不是立即就寫入磁碟,也可以在作業系統的快取裡。如果需要立即刷盤,則呼叫
channel.force(true);
即可。
怎麼用的呢?其實跟之前兩個差不多:
public static void main(String[] args) throws IOException { // 指定需要生成的檔名稱 String targetFileName = "target-file.txt"; // 建立 RandomAccessFile, 賦予可讀(r)、可寫(w)的許可權 RandomAccessFile accessFile = new RandomAccessFile(targetFileName, "rw"); FileChannel fileChannel = accessFile.getChannel(); // 建立 ByteBuffer 並寫入資料 ByteBuffer buffer = ByteBuffer.allocate(16); buffer.put("shDEQuanZhanBiJi".getBytes(StandardCharsets.UTF_8)); // 切換到 buffer 的讀模式 buffer.flip(); // 呼叫 write 將 buffer 的資料寫入到 channel, channel 再寫資料到磁碟檔案 fileChannel.write(buffer); // 相當於清空 buffer buffer.clear(); // 將之前寫入到 channel 的資料再讀入到 buffer fileChannel.read(buffer); // 列印 buffer 中的內容 printBuffer(buffer); fileChannel.close(); }
執行之後的效果就是,會生成一個名為 target-file.txt
的檔案,內容就是 shDEQuanZhanBiJi
。並且控制檯會將之前寫入 channel 的 shDEQuanZhanBiJi
列印出來。
老規矩,細節都在註釋中。值得注意的是 new RandomAccessFile(targetFileName, "rw");
裡的 rw
。註釋裡也寫了,代表賦予可讀、可寫的許可權。
再值得注意的是,你不能說把 rw
改成 w
。
不能這麼玩,因為它就是一個單純的字串匹配,可供選擇的就這麼些:
可以看到,r
必不可少…:
r
只能讀rw
既能讀,也能寫rws
和 rwd
功能和 rw
大致是相同的,可讀、可寫。唯一區別是他們會將每次改動強制刷到磁碟,並且 rws
會將作業系統對該檔案的後設資料也一起刷盤,體現就是檔案的更新時間會更新,而 rwd
不會將檔案的後設資料刷盤由於這倆一個負責連線傳輸,另一個負責連線的監聽,所以就放在一起來講了。這一小節我們大概要做這件事:
但是為了能讓大家直接執行起來,使用者端這側就不從磁碟檔案讀取了,直接用 ByteBuffer。大家可以執行起來之後,自己嘗試從磁碟上去載入。還是先看程式碼,首先是伺服器的:
public static void main(String[] args) throws IOException { // 開啟一個 ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 繫結 8080 埠 serverSocketChannel.bind(new InetSocketAddress(8080)); // 開始接受使用者端連線 SocketChannel socketChannel = serverSocketChannel.accept(); // 獲取連線成功 System.out.printf("socketChannel %s connectedn", socketChannel); // 準備 ByteBuffer 以從 socketChannel 中讀取資料 ByteBuffer buffer = ByteBuffer.allocate(16); // 開始讀取資料 System.out.println("before read"); int read = socketChannel.read(buffer); System.out.printf("read complete, read bytes length: %s n", read); printBuffer(buffer); }
這裡我們使用的是 Java NIO 中預設的阻塞模式,僅僅作為一個掩飾,如果想要 ServerSocketChannel 進入非阻塞模式,可在 open
之後,呼叫:
serverSocketChannel.configureBlocking(false);
由於我們這裡是阻塞模式,所以在程式碼執行到 serverSocketChannel.accept();
時,會陷入阻塞狀態,直到有使用者端過來建立連線。同理,read
方法也是阻塞的,如果使用者端一直沒有寫入資料,那麼伺服器就會一直阻塞在 read
。
直接先給程式碼:
public static void main(String[] args) throws IOException { // 開啟一個 SocketChannel SocketChannel socketChannel = SocketChannel.open(); // 連線到 localhost 的 8080 埠 socketChannel.connect(new InetSocketAddress("localhost", 8080)); // 準備 ByteBuffer ByteBuffer buffer = ByteBuffer.allocate(16); buffer.put(Charset.defaultCharset().encode("test")); // 將 buffer 切換成讀模式 & 向 channel 中寫入資料 buffer.flip(); socketChannel.write(buffer); }
先啟動伺服器,再啟動使用者端。可以看到伺服器側的控制檯有如下的輸出:
socketChannel java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:64373] connected before read read complete, read bytes length: 4 BUFFER VALUE: test
這個就比較簡單,首先是使用者端的程式碼:
public static void main(String[] args) throws IOException { DatagramChannel datagramChannel = DatagramChannel.open(); // 構建 buffer 資料 ByteBuffer buffer = ByteBuffer.allocate(16); buffer.put(Charset.defaultCharset().encode("test")); // 切換到 buffer 的讀模式 buffer.flip(); datagramChannel.send(buffer, new InetSocketAddress("localhost", 8080)); }
然後是伺服器:
public static void main(String[] args) throws IOException { DatagramChannel datagramChannel = DatagramChannel.open(); datagramChannel.bind(new InetSocketAddress(8080)); ByteBuffer buffer = ByteBuffer.allocate(16); datagramChannel.receive(buffer); printBuffer(buffer); }
到此這篇關於Java NIO Channel 使用詳情的文章就介紹到這了,更多相關Java NIO Channel內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45