Java NIO系列教程(二) Channel

原文链接     作者:Jakob Jenkov     译者:airu     校对:丁一

Java NIO的通道类似流,但又有些不同:

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  • 通道可以异步地读写。
  • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

正如上面所说,从通道读取数据到缓冲区,从缓冲区写入数据到通道。如下图所示:

Channel的实现

这些是Java NIO中最重要的通道的实现:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

FileChannel 从文件中读写数据。

DatagramChannel 能通过UDP读写网络中的数据。

SocketChannel 能通过TCP读写网络中的数据。

ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

基本的 Channel 示例

下面是一个使用FileChannel读取数据到Buffer中的示例:

[code lang=”java”]
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {

System.out.println("Read " + bytesRead);
buf.flip();

while(buf.hasRemaining()){
System.out.print((char) buf.get());
}

buf.clear();
bytesRead = inChannel.read(buf);
}
aFile.close();
[/code]

注意 buf.flip() 的调用,首先读取数据到Buffer,然后反转Buffer,接着再从Buffer中读取数据。下一节会深入讲解Buffer的更多细节。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Java NIO系列教程(二) Channel

  • Trackback 关闭
  • 评论 (27)
    • lms
    • 2013/06/11 4:45下午

    基本的 Channel 示例中,为什么还要执行这一句:bytesRead = inChannel.read(buf);(第17行)

      • airu
      • 2013/06/24 4:03下午

      这句表示处理完后继续从channel中读取数据。并无他意。

      • 匿名
      • 2014/02/20 9:22上午

      这你都看不懂,前面循环体里把所有的数据都读取完了,bytesRead = inChannel.read(buf);是为了返回-1无数据可读退出循环体

  1. alert(“test”);

      • iostreamin
      • 2013/10/23 4:43下午

      好坏的童鞋。。。

    • 张三丰
    • 2014/02/20 9:47上午

    inChannel为何不关闭?

      • junfduan
      • 2014/10/28 10:37下午

      因为RandomAccessFile的close方法会将对应的非空channel关闭。(参看jdk源码)

    • 匿名
    • 2014/05/08 10:09下午

    这个例子能行么 如果文件大于48呢

      • fiasco0407
      • 2014/07/07 12:45下午

      buf.clear(); 会清空buf中的数据,然后bytesRead = inChannel.read(buf);会把通道中的数据再次读入到buf中,所以文件大于48木有问题。

  2. 为何要用随机流获取channel

    • 也可以使用其他流的,因为FileChannel只能通过流的Channel获得

    • 昵称
    • 2014/08/15 10:06上午

    alert(‘test’);

      • yukjin
      • 2014/12/31 9:54上午
        • yukjin
        • 2014/12/31 9:55上午

        ss

    • alert(‘test’);
    • 2014/08/15 10:07上午

    alert(‘test’);

    • 匿名
    • 2014/09/26 1:42下午

    alert(‘haha’);

    • 匿名
    • 2014/09/26 1:43下午
    • halu126
    • 2014/10/20 9:22下午

    alert(“test”);

  3. 谢谢好文!
    “注意 buf.flip() 的调用,首先读取数据到Buffer,然后反转Buffer,接着再从Buffer中读取数据”直译成“反转”好像很容易歧义吧,从代码上下文看该方法的功能应该是把指针移到开始位置。
    看源码,也是这个意思。
    public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
    }

  4. idouba :
    谢谢好文!
    “注意 buf.flip() 的调用,首先读取数据到Buffer,然后反转Buffer,接着再从Buffer中读取数据”直译成“反转”好像很容易歧义吧,从代码上下文看该方法的功能应该是把指针移到开始位置。
    看源码,也是这个意思。
    public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
    }

    不好意思自己理解错了,仔细看下源码周边,应该是”切换“和”反转“的意思,表示表示Buffer从写状态切换到读状态。即把limit设置成当前位置,即写操作写到位置;position设置为0,表示从头读,mark标记清除掉。
    LZ请指正啊。。

      • scvthedefect
      • 2016/02/02 8:20下午

      不太能理解楼主为何会把flip翻译为反转,这里应该是重置的意思吧。

      flip()的具体操作就是把Buffer的limit固定到当前的position,然后把position归零。

      • alfredtian
      • 2018/02/22 5:33下午

      是的,不过在这里只是重置了position,以供buf.hasRemaining()使用

    • huangzq
    • 2018/01/27 11:25上午

    alert(‘test’);

    • xupeng.zhang
    • 2018/01/30 9:44上午

    代码建议格式化一下,不然很难看

    • husky
    • 2018/08/13 10:36上午

    写了一个可以读中文的代码
    @Test
    public void channelTest() throws IOException {
    // System.out.println(System.getProperty(“user.dir”));
    RandomAccessFile aFile = new RandomAccessFile(“data/nio-data.txt”,”rw”);
    FileChannel inChannel = aFile.getChannel();

    ByteBuffer buf = ByteBuffer.allocate(48);

    int byteRead = inChannel.read(buf);
    while(byteRead != -1){
    System.out.println(“Read “+byteRead);
    buf.flip();
    byte[] bytes = new byte[byteRead];
    int index = 0;
    while(buf.hasRemaining()){
    bytes[index] = buf.get();
    index ++;
    }
    System.out.println(new String(bytes,”utf-8”));
    buf.clear();
    byteRead = inChannel.read(buf);
    }
    aFile.close();
    }

    • Arry0624
    • 2019/01/09 5:39下午

    alert(“2开花”);

    • jasonpg
    • 2019/03/24 3:42下午

    package demo.nio;

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

    /*
    * 该程序步骤:
    * 1. 读取文件获取FileChannel;
    * 2. 将ByteBuffer分配大小,并得到ByteBuffer
    * 3. 将通道的数据读取到buffer(读取的数据是将数据一次性读到buffer,如果buffer太小,那么读到的文件数据就会缺失);返回值为读取数,读完为-1;
    * 4. 将buffer中的limit定位到文件尾,也就是如果文件大小229,你设置buffer的limit为1000,他会将limit设为229;
    * 5. 如果读取位置和limit之间还有数据,打印byte数据
    * 6. 关闭buffer;
    * 7. 关闭channel;
    */

    public class FileChannelDemo {

    public static void main(String[] args) throws IOException {
    RandomAccessFile aFile = new RandomAccessFile(“C:\\Users\\28256\\Desktop\\test.txt”, “rw”);
    FileChannel inChannel = aFile.getChannel();

    //分配缓存区大小
    ByteBuffer buf = ByteBuffer.allocate(1000);
    int bytesRead = 0;
    while((bytesRead = inChannel.read(buf)) != -1) {
    System.out.println(“Read” + bytesRead);
    buf.flip();

    while(buf.hasRemaining()) {
    System.out.println((char)buf.get());
    }

    buf.clear();
    inChannel.read(buf);
    }
    aFile.close();

    }

    }

return top