成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Java NIO 之 Channel(通道)

piglei / 954人閱讀

摘要:通道可以異步讀寫(xiě)。使用的方法讀取數(shù)據(jù)創(chuàng)建一個(gè)讀數(shù)據(jù)緩沖區(qū)對(duì)象從通道中讀取數(shù)據(jù)使用的方法寫(xiě)入數(shù)據(jù)創(chuàng)建一個(gè)寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象寫(xiě)入數(shù)據(jù)關(guān)閉完成使用后,您必須關(guān)閉它。五提供了一種被稱(chēng)為的新功能,也稱(chēng)為本地矢量。功能是通道提供的并不是。

歷史回顧:

Java NIO 概覽

Java NIO 之 Buffer(緩沖區(qū))

其他高贊文章:

面試中關(guān)于Redis的問(wèn)題看這篇就夠了

一文輕松搞懂redis集群原理及搭建與使用

一 Channel(通道)介紹

通常來(lái)說(shuō)NIO中的所有IO都是從 Channel(通道) 開(kāi)始的。

從通道進(jìn)行數(shù)據(jù)讀取 :創(chuàng)建一個(gè)緩沖區(qū),然后請(qǐng)求通道讀取數(shù)據(jù)。

從通道進(jìn)行數(shù)據(jù)寫(xiě)入 :創(chuàng)建一個(gè)緩沖區(qū),填充數(shù)據(jù),并要求通道寫(xiě)入數(shù)據(jù)。

數(shù)據(jù)讀取和寫(xiě)入操作圖示:

Java NIO Channel通道和流非常相似,主要有以下幾點(diǎn)區(qū)別:

通道可以讀也可以寫(xiě),流一般來(lái)說(shuō)是單向的(只能讀或者寫(xiě),所以之前我們用流進(jìn)行IO操作的時(shí)候需要分別創(chuàng)建一個(gè)輸入流和一個(gè)輸出流)。

通道可以異步讀寫(xiě)。

通道總是基于緩沖區(qū)Buffer來(lái)讀寫(xiě)。

Java NIO中最重要的幾個(gè)Channel的實(shí)現(xiàn):

FileChannel: 用于文件的數(shù)據(jù)讀寫(xiě)

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

SocketChannel: 用于TCP的數(shù)據(jù)讀寫(xiě),一般是客戶(hù)端實(shí)現(xiàn)

ServerSocketChannel: 允許我們監(jiān)聽(tīng)TCP鏈接請(qǐng)求,每個(gè)請(qǐng)求會(huì)創(chuàng)建會(huì)一個(gè)SocketChannel,一般是服務(wù)器實(shí)現(xiàn)

類(lèi)層次結(jié)構(gòu):

下面的UML圖使用Idea生成的。

二 FileChannel的使用

使用FileChannel讀取數(shù)據(jù)到Buffer(緩沖區(qū))以及利用Buffer(緩沖區(qū))寫(xiě)入數(shù)據(jù)到FileChannel:

package filechannel;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileChannelTxt {
    public static void main(String args[]) throws IOException {
        //1.創(chuàng)建一個(gè)RandomAccessFile(隨機(jī)訪問(wèn)文件)對(duì)象,
        RandomAccessFile raf=new RandomAccessFile("D:
iodata.txt", "rw");
        //通過(guò)RandomAccessFile對(duì)象的getChannel()方法。FileChannel是抽象類(lèi)。
        FileChannel inChannel=raf.getChannel();
        //2.創(chuàng)建一個(gè)讀數(shù)據(jù)緩沖區(qū)對(duì)象
        ByteBuffer buf=ByteBuffer.allocate(48);
        //3.從通道中讀取數(shù)據(jù)
        int bytesRead = inChannel.read(buf);
        //創(chuàng)建一個(gè)寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象
        ByteBuffer buf2=ByteBuffer.allocate(48);
        //寫(xiě)入數(shù)據(jù)
        buf2.put("filechannel test".getBytes());
        buf2.flip();
        inChannel.write(buf);
        while (bytesRead != -1) {

            System.out.println("Read " + bytesRead);
            //Buffer有兩種模式,寫(xiě)模式和讀模式。在寫(xiě)模式下調(diào)用flip()之后,Buffer從寫(xiě)模式變成讀模式。
            buf.flip();
           //如果還有未讀內(nèi)容
            while (buf.hasRemaining()) {
                System.out.print((char) buf.get());
            }
            //清空緩存區(qū)
            buf.clear();
            bytesRead = inChannel.read(buf);
        }
        //關(guān)閉RandomAccessFile(隨機(jī)訪問(wèn)文件)對(duì)象
        raf.close();
    }
}

運(yùn)行效果:

通過(guò)上述實(shí)例代碼,我們可以大概總結(jié)出FileChannel的一般使用規(guī)則:
1. 開(kāi)啟FileChannel

使用之前,F(xiàn)ileChannel必須被打開(kāi) ,但是你無(wú)法直接打開(kāi)FileChannel(FileChannel是抽象類(lèi))。需要通過(guò) InputStream , OutputStreamRandomAccessFile 獲取FileChannel。

我們上面的例子是通過(guò)RandomAccessFile打開(kāi)FileChannel的:

        //1.創(chuàng)建一個(gè)RandomAccessFile(隨機(jī)訪問(wèn)文件)對(duì)象,
        RandomAccessFile raf=new RandomAccessFile("D:
iodata.txt", "rw");
        //通過(guò)RandomAccessFile對(duì)象的getChannel()方法。FileChannel是抽象類(lèi)。
        FileChannel inChannel=raf.getChannel();
2. 從FileChannel讀取數(shù)據(jù)/寫(xiě)入數(shù)據(jù)

從FileChannel中讀取數(shù)據(jù)/寫(xiě)入數(shù)據(jù)之前首先要?jiǎng)?chuàng)建一個(gè)Buffer(緩沖區(qū))對(duì)象,Buffer(緩沖區(qū))對(duì)象的使用我們?cè)谏弦黄恼轮幸呀?jīng)詳細(xì)說(shuō)明了,如果不了解的話可以看我的上一篇關(guān)于Buffer的文章。

使用FileChannel的read()方法讀取數(shù)據(jù):

        //2.創(chuàng)建一個(gè)讀數(shù)據(jù)緩沖區(qū)對(duì)象
        ByteBuffer buf=ByteBuffer.allocate(48);
        //3.從通道中讀取數(shù)據(jù)
        int bytesRead = inChannel.read(buf);

使用FileChannel的write()方法寫(xiě)入數(shù)據(jù):

        //創(chuàng)建一個(gè)寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象
        ByteBuffer buf2=ByteBuffer.allocate(48);
        //寫(xiě)入數(shù)據(jù)
        buf2.put("filechannel test".getBytes());
        buf2.flip();
        inChannel.write(buf);
3. 關(guān)閉FileChannel

完成使用后,F(xiàn)ileChannel您必須關(guān)閉它。

channel.close();    
三 SocketChannel和ServerSocketChannel的使用

利用SocketChannel和ServerSocketChannel實(shí)現(xiàn)客戶(hù)端與服務(wù)器端簡(jiǎn)單通信:

SocketChannel 用于創(chuàng)建基于tcp協(xié)議的客戶(hù)端對(duì)象,因?yàn)镾ocketChannel中不存在accept()方法,所以,它不能成為一個(gè)服務(wù)端程序。通過(guò) connect()方法 ,SocketChannel對(duì)象可以連接到其他tcp服務(wù)器程序。

客戶(hù)端:

package socketchannel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class WebClient {
    public static void main(String[] args) throws IOException {
        //1.通過(guò)SocketChannel的open()方法創(chuàng)建一個(gè)SocketChannel對(duì)象
        SocketChannel socketChannel = SocketChannel.open();
        //2.連接到遠(yuǎn)程服務(wù)器(連接此通道的socket)
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 3333));
        // 3.創(chuàng)建寫(xiě)數(shù)據(jù)緩存區(qū)對(duì)象
        ByteBuffer writeBuffer = ByteBuffer.allocate(128);
        writeBuffer.put("hello WebServer this is from WebClient".getBytes());
        writeBuffer.flip();
        socketChannel.write(writeBuffer);
        //創(chuàng)建讀數(shù)據(jù)緩存區(qū)對(duì)象
        ByteBuffer readBuffer = ByteBuffer.allocate(128);
        socketChannel.read(readBuffer);
        //String 字符串常量,不可變;StringBuffer 字符串變量(線程安全),可變;StringBuilder 字符串變量(非線程安全),可變
        StringBuilder stringBuffer=new StringBuilder();
        //4.將Buffer從寫(xiě)模式變?yōu)榭勺x模式
        readBuffer.flip();
        while (readBuffer.hasRemaining()) {
            stringBuffer.append((char) readBuffer.get());
        }
        System.out.println("從服務(wù)端接收到的數(shù)據(jù):"+stringBuffer);

        socketChannel.close();
    }

}

ServerSocketChannel 允許我們監(jiān)聽(tīng)TCP鏈接請(qǐng)求,通過(guò)ServerSocketChannelImpl的 accept()方法 可以創(chuàng)建一個(gè)SocketChannel對(duì)象用戶(hù)從客戶(hù)端讀/寫(xiě)數(shù)據(jù)。

服務(wù)端:

package socketchannel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class WebServer {
    public static void main(String args[]) throws IOException {
        try {
            //1.通過(guò)ServerSocketChannel 的open()方法創(chuàng)建一個(gè)ServerSocketChannel對(duì)象,open方法的作用:打開(kāi)套接字通道
            ServerSocketChannel ssc = ServerSocketChannel.open();
            //2.通過(guò)ServerSocketChannel綁定ip地址和port(端口號(hào))
            ssc.socket().bind(new InetSocketAddress("127.0.0.1", 3333));
            //通過(guò)ServerSocketChannelImpl的accept()方法創(chuàng)建一個(gè)SocketChannel對(duì)象用戶(hù)從客戶(hù)端讀/寫(xiě)數(shù)據(jù)
            SocketChannel socketChannel = ssc.accept();
            //3.創(chuàng)建寫(xiě)數(shù)據(jù)的緩存區(qū)對(duì)象
            ByteBuffer writeBuffer = ByteBuffer.allocate(128);
            writeBuffer.put("hello WebClient this is from WebServer".getBytes());
            writeBuffer.flip();
            socketChannel.write(writeBuffer);
            //創(chuàng)建讀數(shù)據(jù)的緩存區(qū)對(duì)象
            ByteBuffer readBuffer = ByteBuffer.allocate(128);
            //讀取緩存區(qū)數(shù)據(jù)
            socketChannel.read(readBuffer);
            StringBuilder stringBuffer=new StringBuilder();
            //4.將Buffer從寫(xiě)模式變?yōu)榭勺x模式
            readBuffer.flip();
            while (readBuffer.hasRemaining()) {
                stringBuffer.append((char) readBuffer.get());
            }
            System.out.println("從客戶(hù)端接收到的數(shù)據(jù):"+stringBuffer);
            socketChannel.close();
            ssc.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

運(yùn)行效果:

客戶(hù)端:

服務(wù)端:

通過(guò)上述實(shí)例代碼,我們可以大概總結(jié)出SocketChannel和ServerSocketChannel的使用的一般使用規(guī)則:

考慮到篇幅問(wèn)題,下面只給出大致步驟,不貼代碼,可以結(jié)合上述實(shí)例理解。

客戶(hù)端
1.通過(guò)SocketChannel連接到遠(yuǎn)程服務(wù)器
2.創(chuàng)建讀數(shù)據(jù)/寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象來(lái)讀取服務(wù)端數(shù)據(jù)或向服務(wù)端發(fā)送數(shù)據(jù)
3.關(guān)閉SocketChannel
服務(wù)端
1.通過(guò)ServerSocketChannel 綁定ip地址和端口號(hào)
2.通過(guò)ServerSocketChannelImpl的accept()方法創(chuàng)建一個(gè)SocketChannel對(duì)象用戶(hù)從客戶(hù)端讀/寫(xiě)數(shù)據(jù)
3.創(chuàng)建讀數(shù)據(jù)/寫(xiě)數(shù)據(jù)緩沖區(qū)對(duì)象來(lái)讀取客戶(hù)端數(shù)據(jù)或向客戶(hù)端發(fā)送數(shù)據(jù)
4. 關(guān)閉SocketChannel和ServerSocketChannel
四 ?DatagramChannel的使用

DataGramChannel,類(lèi)似于java 網(wǎng)絡(luò)編程的DatagramSocket類(lèi);使用UDP進(jìn)行網(wǎng)絡(luò)傳輸, UDP是無(wú)連接,面向數(shù)據(jù)報(bào)文段的協(xié)議,對(duì)傳輸?shù)臄?shù)據(jù)不保證安全與完整 ;和上面介紹的SocketChannel和ServerSocketChannel的使用方法類(lèi)似,所以這里就簡(jiǎn)單介紹一下如何使用。

1.獲取DataGramChannel
        //1.通過(guò)DatagramChannel的open()方法創(chuàng)建一個(gè)DatagramChannel對(duì)象
        DatagramChannel datagramChannel = DatagramChannel.open();
        //綁定一個(gè)port(端口)
        datagramChannel.bind(new InetSocketAddress(1234));

上面代碼表示程序可以在1234端口接收數(shù)據(jù)報(bào)。

2.接收/發(fā)送消息

接收消息:

先創(chuàng)建一個(gè)緩存區(qū)對(duì)象,然后通過(guò)receive方法接收消息,這個(gè)方法返回一個(gè)SocketAddress對(duì)象,表示發(fā)送消息方的地址:

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);

發(fā)送消息:

由于UDP下,服務(wù)端和客戶(hù)端通信并不需要建立連接,只需要知道對(duì)方地址即可發(fā)出消息,但是是否發(fā)送成功或者成功被接收到是沒(méi)有保證的;發(fā)送消息通過(guò)send方法發(fā)出,改方法返回一個(gè)int值,表示成功發(fā)送的字節(jié)數(shù):

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put("datagramchannel".getBytes());
buf.flip();
int send = channel.send(buffer, new InetSocketAddress("localhost",1234));

這個(gè)例子發(fā)送一串字符:“datagramchannel”到主機(jī)名為”localhost”服務(wù)器的端口1234上。

五 Scatter / Gather

Channel 提供了一種被稱(chēng)為 Scatter/Gather 的新功能,也稱(chēng)為本地矢量 I/O。Scatter/Gather 是指在多個(gè)緩沖區(qū)上實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 I/O 操作。正確使用 Scatter / Gather可以明顯提高性能。

大多數(shù)現(xiàn)代操作系統(tǒng)都支持本地矢量I/O(native vectored I/O)操作。當(dāng)您在一個(gè)通道上請(qǐng)求一個(gè)Scatter/Gather操作時(shí),該請(qǐng)求會(huì)被翻譯為適當(dāng)?shù)谋镜卣{(diào)用來(lái)直接填充或抽取緩沖區(qū),減少或避免了緩沖區(qū)拷貝和系統(tǒng)調(diào)用;

Scatter/Gather應(yīng)該使用直接的ByteBuffers以從本地I/O獲取最大性能優(yōu)勢(shì)。

Scatter/Gather功能是通道(Channel)提供的 并不是Buffer。

Scatter: 從一個(gè)Channel讀取的信息分散到N個(gè)緩沖區(qū)中(Buufer).

Gather: 將N個(gè)Buffer里面內(nèi)容按照順序發(fā)送到一個(gè)Channel.

Scattering Reads

"scattering read"是把數(shù)據(jù)從單個(gè)Channel寫(xiě)入到多個(gè)buffer,如下圖所示:


示例代碼:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = { header, body };

channel.read(bufferArray);

read()方法內(nèi)部會(huì)負(fù)責(zé)把數(shù)據(jù)按順序?qū)戇M(jìn)傳入的buffer數(shù)組內(nèi)。一個(gè)buffer寫(xiě)滿(mǎn)后,接著寫(xiě)到下一個(gè)buffer中。

舉個(gè)例子,假如通道中有200個(gè)字節(jié)數(shù)據(jù),那么header會(huì)被寫(xiě)入128個(gè)字節(jié)數(shù)據(jù),body會(huì)被寫(xiě)入72個(gè)字節(jié)數(shù)據(jù);

注意:

無(wú)論是scatter還是gather操作,都是按照buffer在數(shù)組中的順序來(lái)依次讀取或?qū)懭氲模?/p> Gathering Writes

"gathering write"把多個(gè)buffer的數(shù)據(jù)寫(xiě)入到同一個(gè)channel中,下面是示意圖:

示例代碼:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

//write data into buffers

ByteBuffer[] bufferArray = { header, body };

channel.write(bufferArray);

write()方法內(nèi)部會(huì)負(fù)責(zé)把數(shù)據(jù)按順序?qū)懭氲絚hannel中。

注意:

并不是所有數(shù)據(jù)都寫(xiě)入到通道,寫(xiě)入的數(shù)據(jù)要根據(jù)position和limit的值來(lái)判斷,只有position和limit之間的數(shù)據(jù)才會(huì)被寫(xiě)入;

舉個(gè)例子,假如以上header緩沖區(qū)中有128個(gè)字節(jié)數(shù)據(jù),但此時(shí)position=0,limit=58;那么只有下標(biāo)索引為0-57的數(shù)據(jù)才會(huì)被寫(xiě)入到通道中。

六 通道之間的數(shù)據(jù)傳輸

在Java NIO中如果一個(gè)channel是FileChannel類(lèi)型的,那么他可以直接把數(shù)據(jù)傳輸?shù)搅硪粋€(gè)channel。

transferFrom() :transferFrom方法把數(shù)據(jù)從通道源傳輸?shù)紽ileChannel

transferTo() :transferTo方法把FileChannel數(shù)據(jù)傳輸?shù)搅硪粋€(gè)channel

參考:

官方JDK相關(guān)文檔

谷歌搜索排名第一的Java NIO教程

《Java NIO》

《Java 8編程官方參考教程(第9版)》

歡迎關(guān)注我的微信公眾號(hào):"Java面試通關(guān)手冊(cè)"(一個(gè)有溫度的微信公眾號(hào),期待與你共同進(jìn)步~~~堅(jiān)持原創(chuàng),分享美文,分享各種Java學(xué)習(xí)資源):

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69385.html

相關(guān)文章

  • Java NIOSelector(選擇器)

    摘要:抽象類(lèi)有一個(gè)方法用于使通道處于阻塞模式或非阻塞模式。注意抽象類(lèi)的方法是由抽象類(lèi)實(shí)現(xiàn)的,都是直接繼承了抽象類(lèi)。大家有興趣可以看看的源碼,各種抽象類(lèi)和抽象類(lèi)上層的抽象類(lèi)。 歷史回顧: Java NIO 概覽 Java NIO 之 Buffer(緩沖區(qū)) Java NIO 之 Channel(通道) 其他高贊文章: 面試中關(guān)于Redis的問(wèn)題看這篇就夠了 一文輕松搞懂redis集群原理及搭建...

    xiaokai 評(píng)論0 收藏0
  • 關(guān)于Java IO與NIO知識(shí)都在這里

    摘要:從通道進(jìn)行數(shù)據(jù)寫(xiě)入創(chuàng)建一個(gè)緩沖區(qū),填充數(shù)據(jù),并要求通道寫(xiě)入數(shù)據(jù)。三之通道主要內(nèi)容通道介紹通常來(lái)說(shuō)中的所有都是從通道開(kāi)始的。從中選擇選擇器維護(hù)注冊(cè)過(guò)的通道的集合,并且這種注冊(cè)關(guān)系都被封裝在當(dāng)中停止選擇的方法方法和方法。 由于內(nèi)容比較多,我下面放的一部分是我更新在我的微信公眾號(hào)上的鏈接,微信排版比較好看,更加利于閱讀。每一篇文章下面我都把文章的主要內(nèi)容給列出來(lái)了,便于大家學(xué)習(xí)與回顧。 Ja...

    Riddler 評(píng)論0 收藏0
  • Java IONIO

    摘要:上篇說(shuō)了最基礎(chǔ)的五種模型,相信大家對(duì)相關(guān)的概念應(yīng)該有了一定的了解,這篇文章主要講講基于多路復(fù)用的。 上篇說(shuō)了最基礎(chǔ)的五種IO模型,相信大家對(duì)IO相關(guān)的概念應(yīng)該有了一定的了解,這篇文章主要講講基于多路復(fù)用IO的Java NIO。 背景 Java誕生至今,有好多種IO模型,從最早的Java IO到后來(lái)的Java NIO以及最新的Java AIO,每種IO模型都有它自己的特點(diǎn),詳情請(qǐng)看我的上...

    pingink 評(píng)論0 收藏0
  • 動(dòng)力節(jié)點(diǎn)JavaNIO教程,輕松攻破Java NIO技術(shù)壁壘

    摘要:學(xué)習(xí)和掌握技術(shù)已經(jīng)不是一個(gè)攻城獅的加分技能,而是一個(gè)必備技能。是雙向的,不僅可以讀取數(shù)據(jù)還能保存數(shù)據(jù),程序不能直接讀寫(xiě)通道,只與緩沖區(qū)交互為了讓大家不被高并發(fā)與大量連接處理問(wèn)題所困擾,動(dòng)力節(jié)點(diǎn)推出了高效處理模型應(yīng)用教程。 大家肯定了解Java IO, 但是對(duì)于NIO一般是陌生的,而現(xiàn)在使用到NIO的場(chǎng)景越來(lái)越多,很多技術(shù)框...

    ralap 評(píng)論0 收藏0
  • Java NIO 概覽

    摘要:線程之間的切換對(duì)于操作系統(tǒng)來(lái)說(shuō)是昂貴的。因此,單線程可以監(jiān)視多個(gè)通道中的數(shù)據(jù)。當(dāng)方法返回后,線程可以處理這些事件。 一 NIO簡(jiǎn)介 Java NIO 是 java 1.4 之后新出的一套IO接口,這里的的新是相對(duì)于原有標(biāo)準(zhǔn)的Java IO和Java Networking接口。NIO提供了一種完全不同的操作方式。 NIO中的N可以理解為Non-blocking,不單純是New。 它支持面...

    chemzqm 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<