日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

NIO详解(九):Channel详解

發布時間:2025/4/16 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NIO详解(九):Channel详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. Channel概述

Java NIO的通道類似流,但又有些不同。

  • Channel既可以從通道中讀取數據,又可以寫數據到通道。但流的讀寫通常是單向的。
  • Channel通道可以異步地讀寫。
  • Channel通道中的數據總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入。
  • 通道中的數據總是要先讀到一個Buffer,或者總是要從一個Buffer中寫入。

    1.1 Channel 的實現

    這些是Java NIO中最重要的通道的實現:

    • FileChannel: FileChannel 從文件中讀寫數據。
    • DatagramChannel:DatagramChannel 能通過UDP讀寫網絡中的數據。
    • SocketChannel:SocketChannel 能通過TCP讀寫網絡中的數據。
    • ServerSocketChannel:ServerSocketChannel可以監聽新進來的TCP連接,像Web服務器那樣。對每一個新進來的連接都會創建一個SocketChannel。
    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel();//create buffer with capacity of 48 bytes ByteBuffer buf = ByteBuffer.allocate(48);int bytesRead = 0; //read into buffer. while ((bytesRead=inChannel.read(buf))!=-1) {buf.flip(); //make buffer ready for readwhile(buf.hasRemaining()){System.out.print((char) buf.get()); // read 1 byte at a time}buf.clear(); //make buffer ready for writing } aFile.close();

    2. Channel 數據傳輸之Scatter/Gather模式

    Java NIO開始支持scatter/gather,scatter/gather用于描述從Channe中讀取或者寫入到Channel的操作。

    分散(scatter)從Channel中讀取是指在讀操作時將讀取的數據寫入多個buffer中。因此,Channel將從Channel中讀取的數據“分散(scatter)”到多個Buffer中。

    聚集(gather)寫入Channel是指在寫操作時將多個buffer的數據寫入同一個Channel,因此,Channel 將多個Buffer中的數據“聚集(gather)”后發送到Channel。

    scatter / gather經常用于需要將傳輸的數據分開處理的場合,例如傳輸一個由消息頭和消息體組成的消息,你可能會將消息體和消息頭分散到不同的buffer中,這樣你可以方便的處理消息頭和消息體。

    2.1 Scattering Reads

    Scattering Reads是指數據從一個channel讀取到多個buffer中。如下圖描述:

    ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024);ByteBuffer[] bufferArray = { header, body };channel.read(bufferArray);

    buffer首先被插入到數組,然后再將數組作為channel.read() 的輸入參數。read()方法按照buffer在數組中的順序將從channel中讀取的數據寫入到buffer,當一個buffer被寫滿后,channel緊接著向另一個buffer中寫。Scattering Reads在移動下一個buffer前,必須填滿當前的buffer,這也意味著它不適用于動態消息(譯者注:消息大小不固定)。換句話說,如果存在消息頭和消息體,消息頭必須完成填充(例如 128byte),Scattering Reads才能正常工作。

    2.2 Gathering Writes

    Gathering Writes是指數據從多個buffer寫入到同一個channel。如下圖描述:

    ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024);//write data into buffersByteBuffer[] bufferArray = { header, body };channel.write(bufferArray);

    buffers數組是write()方法的入參,write()方法會按照buffer在數組中的順序,將數據寫入到channel。

    3. ServerSocketChannel

    Java NIO中的 ServerSocketChannel 是一個可以監聽新進來的TCP連接的通道, 就像標準IO中的ServerSocket一樣。ServerSocketChannel類在 java.nio.channels包中。

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9999));while(true){SocketChannel socketChannel =serverSocketChannel.accept();//do something with socketChannel... }

    3.1 打開 ServerSocketChannel

    通過調用 ServerSocketChannel.open() 方法來打開ServerSocketChannel.如:

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    3.2 關閉 ServerSocketChannel

    通過調用ServerSocketChannel.close() 方法來關閉ServerSocketChannel. 如:

    serverSocketChannel.close();

    3.3 監聽新進來的連接

    通過 ServerSocketChannel.accept() 方法監聽新進來的連接。當 accept()方法返回的時候,它返回一個包含新進來的連接的 SocketChannel。因此, accept()方法會一直阻塞到有新連接到達。

    通常不會僅僅只監聽一個連接,在while循環中調用 accept()方法. 如下面的例子:

    while(true){SocketChannel socketChannel =serverSocketChannel.accept();//do something with socketChannel... }

    3.4 ServerSocketChannel非阻塞模式

    ServerSocketChannel可以設置成非阻塞模式。在非阻塞模式下,accept() 方法會立刻返回,如果還沒有新進來的連接,返回的將是null。 因此,需要檢查返回的SocketChannel是否是null.

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9999)); serverSocketChannel.configureBlocking(false);while(true){SocketChannel socketChannel =serverSocketChannel.accept();if(socketChannel != null){//do something with socketChannel...} }

    4. SocketChannel

    Java NIO中的SocketChannel是一個連接到TCP網絡套接字的通道。可以通過以下2種方式創建SocketChannel:

  • 打開一個SocketChannel并連接到互聯網上的某臺服務器。
  • 一個新連接到達ServerSocketChannel時,會創建一個SocketChannel。
  • 4.1 打開SocketChannel

    下面是SocketChannel的打開方式:

    SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("192.168.1.1", 80));

    4.2 關閉 SocketChannel

    當用完SocketChannel之后調用SocketChannel.close()關閉SocketChannel:

    socketChannel.close();

    4.3 從 SocketChannel 讀取數據

    要從SocketChannel中讀取數據,調用一個read()的方法之一。以下是例子:

    ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = socketChannel.read(buf);

    首先,分配一個Buffer。從SocketChannel讀取到的數據將會放到這個Buffer中。然后,調用SocketChannel.read()。該方法將數據從SocketChannel 讀到Buffer中。read()方法返回的int值表示讀了多少字節進Buffer里。如果返回的是-1,表示已經讀到了流的末尾(連接關閉了)。

    4.4 寫入 SocketChannel 寫入數據

    寫數據到SocketChannel用的是SocketChannel.write()方法,該方法以一個Buffer作為參數。示例如下:

    String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put(newData.getBytes());buf.flip();while(buf.hasRemaining()) {channel.write(buf); }

    注意SocketChannel.write()方法的調用是在一個while循環中的。Write()方法無法保證能寫多少字節到SocketChannel。所以,我們重復調用write()直到Buffer沒有要寫的字節為止。

    4.5 非阻塞模式

    可以設置 SocketChannel 為非阻塞模式(non-blocking mode).設置之后,就可以在異步模式下調用connect(), read() 和write()了。

    connect()

    如果SocketChannel在非阻塞模式下,此時調用connect(),該方法可能在連接建立之前就返回了。為了確定連接是否建立,可以調用finishConnect()的方法。像這樣:

    socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("192.168.1.1", 80));while(! socketChannel.finishConnect() ){//wait, or do something else... }

    write()

    非阻塞模式下,write()方法在尚未寫出任何內容時可能就返回了。所以需要在循環中調用write()。前面已經有例子了,這里就不贅述了。

    read()

    非阻塞模式下,read()方法在尚未讀取到任何數據時可能就返回了。所以需要關注它的int返回值,它會告訴你讀取了多少字節。

    非阻塞模式與選擇器

    非阻塞模式與選擇器搭配會工作的更好,通過將一或多個SocketChannel注冊到Selector,可以詢問選擇器哪個通道已經準備好了讀取,寫入等。Selector與SocketChannel的搭配使用會在后面詳講。

    5. FileChannel

    Java NIO中的FileChannel是一個連接到文件的通道。可以通過文件通道讀寫文件。FileChannel無法設置為非阻塞模式,他總是運行在阻塞模式下。

    5.1 打開FileChannel

    在使用FileChannel之前,必須先打開它。但是,我們無法直接打開一個FileChannel,需要通過使用一個InputStream、OutputStream或RandomAccessFile來獲取一個FileChannel實例。下面是通過RandomAccessFile打開FileChannel的示例:

    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel();

    5.2 從FileChannel讀取數據

    ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf);

    首先,分配一個Buffer。從FileChannel中讀取的數據將被讀到Buffer中。然后,調用FileChannel.read()方法。該方法將數據從FileChannel讀取到Buffer中。read()方法返回的int值表示了有多少字節被讀到了Buffer中。如果返回-1,表示到了文件末尾。

    5.3 向FileChannel寫數據

    使用FileChannel.write()方法向FileChannel寫數據,該方法的參數是一個Buffer。如:

    String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put(newData.getBytes());buf.flip();while(buf.hasRemaining()) {channel.write(buf); }

    注意FileChannel.write()是在while循環中調用的。因為無法保證write()方法一次能向FileChannel寫入多少字節,因此需要重復調用write()方法,直到Buffer中已經沒有尚未寫入通道的字節。

    5.4 關閉FileChannel

    用完FileChannel后必須將其關閉。如:

    channel.close();

    5.5 FileChannel的position方法

    有時可能需要在FileChannel的某個特定位置進行數據的讀/寫操作。可以通過調用position()方法獲取FileChannel的當前位置。也可以通過調用position(long pos)方法設置FileChannel的當前位置。

    long pos = channel.position(); channel.position(pos +123);

    如果將位置設置在文件結束符之后,然后試圖從文件通道中讀取數據,讀方法將返回-1 —— 文件結束標志。

    5.6 FileChannel的size方法

    FileChannel實例的size()方法將返回該實例所關聯文件的大小。如:

    long fileSize = channel.size();

    5.7 FileChannel的truncate方法

    可以使用FileChannel.truncate()方法截取一個文件。截取文件時,文件將中指定長度后面的部分將被刪除。如:

    channel.truncate(1024);

    這個例子截取文件的前1024個字節。

    5.8 FileChannel的force方法

    FileChannel.force()方法將通道里尚未寫入磁盤的數據強制寫到磁盤上。出于性能方面的考慮,操作系統會將數據緩存在內存中,所以無法保證寫入到FileChannel里的數據一定會即時寫到磁盤上。要保證這一點,需要調用force()方法。

    force()方法有一個boolean類型的參數,指明是否同時將文件元數據(權限信息等)寫到磁盤上。

    下面的例子同時將文件數據和元數據強制寫到磁盤上:

    channel.force(true);

    6. DatagramChannel

    Java NIO中的DatagramChannel是一個能收發UDP包的通道。因為UDP是無連接的網絡協議,所以不能像其它通道那樣讀取和寫入。它發送和接收的是數據包。

    6.1 打開 DatagramChannel

    下面是 DatagramChannel 的打開方式:

    DatagramChannel channel = DatagramChannel.open(); channel.socket().bind(new InetSocketAddress(9999));

    6.2 DatagramChannel接收數據

    通過receive()方法從DatagramChannel接收數據,如:

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

    receive()方法會將接收到的數據包內容復制到指定的Buffer. 如果Buffer容不下收到的數據,多出的數據將被丟棄。

    6.3 DatagramChannel發送數據

    通過send()方法從DatagramChannel發送數據,如:

    String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put(newData.getBytes()); buf.flip();int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));

    這個例子發送一串字符到”jenkov.com”服務器的UDP端口80。 因為服務端并沒有監控這個端口,所以什么也不會發生。也不會通知你發出的數據包是否已收到,因為UDP在數據傳送方面沒有任何保證。

    6.4 DatagramChannel連接到特定的地址

    可以將DatagramChannel“連接”到網絡中的特定地址的。由于UDP是無連接的,連接到特定地址并不會像TCP通道那樣創建一個真正的連接。而是鎖住DatagramChannel ,讓其只能從特定地址收發數據。

    channel.connect(new InetSocketAddress("192.168.1.1", 80));

    當連接后,也可以使用read()和write()方法,就像在用傳統的通道一樣。只是在數據傳送方面沒有任何保證。這里有幾個例子:

    int bytesRead = channel.read(buf); int bytesWritten = channel.write(but);

    總結

    以上是生活随笔為你收集整理的NIO详解(九):Channel详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。