zoukankan      html  css  js  c++  java
  • Java NIO(三)通道

    概念

    通道(Channel)由java.nio.channels包定义的。channel表示IO源与目标打开的连接,类似流,但不能直接访问数据,只能与Buffer进行交互

    通道类似流,但又有不同:

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

    通常情况下,通道与操作系统的文件描述符(FileDescriptor)和文件句柄(FileHandler)有着一对一的关系。虽然通道比文件描述符更广义,但开发者经常使用到的多数通道都是连接到开放的文件描述符的。通道是一种途径,借助该途径,可以用最小的总开销来访问操作系统本身的I/O服务。缓冲区则是通道内部用来发送和接收数据的端点

    Channel的源码:

    public interface Channel extends Closeable {
    
        /**
         * Tells whether or not this channel is open.  </p>
         *
         * @return <tt>true</tt> if, and only if, this channel is open
         */
        public boolean isOpen();
    
        /**
         * Closes this channel.
         *
         * <p> After a channel is closed, any further attempt to invoke I/O
         * operations upon it will cause a {@link ClosedChannelException} to be
         * thrown.
         *
         * <p> If this channel is already closed then invoking this method has no
         * effect.
         *
         * <p> This method may be invoked at any time.  If some other thread has
         * already invoked it, however, then another invocation will block until
         * the first invocation is complete, after which it will return without
         * effect. </p>
         *
         * @throws  IOException  If an I/O error occurs
         */
        public void close() throws IOException;
    
    }

    和缓冲区不同,通道API主要由接口指定。不同的操作系统上通道实现会有根本性的差异,所以通道API仅仅描述了可以做什么,因此很自然地,通道实现经常使用操作系统的本地代码,通道接口允许开发者以一种受控且可移植的方式来访问底层的I/O服务。可以从底层的Channel接口看到,对所有通道来说只有两种共同的操作:检查一个通道是否打开isOpen()和关闭一个打开的通道close(),其余所有的东西都是那些实现Channel接口以及它的子接口的类。

    通道的基本接口

    public interface ReadableByteChannel extends Channel {
        public int read(ByteBuffer dst) throws IOException;
    }
    public interface WritableByteChannel extends Channel {
        public int write(ByteBuffer src) throws IOException;
    }
    public interface ByteChannel extends ReadableByteChannel, WritableByteChannel{
    
    }

    通道可以是单向的也可以是多向的。一个Channel类可以实现定义了read()方法的ReadableByteChannel接口,也可以实现定义了write()方法的WritableByteChannel接口。实现其中一种为单向,只能在一个方向上传输数据,两种都实现为双向的,可以双向传输数据,如ByteChannel

    Channel的实现

    • FileChannel: 从文件中读写数据
    • DatagramChannel: 能通过UDP读写网络中的数据
    • SocketChannel: 能通过TCP读写网络中的数据
    • ServerSocketChannel: 监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel

    获取通道

    获取通道的一种方式是对支持通道的对象调用getChannel()方法。支持通道的类如下:

    • FileInputStream
    • FileOutputStream
    • RandomAccessFile
    • DatagramSocket
    • Socket
    • ServerSocket

    获取通道的其他方式是使用Files(JDK1.7)类的静态方法newByteChannel()获取字节通道。或者通过通道的静态方法open()打开并返回指定通道

    数据传输:

    int bytesWrite = inChannel.write(buf); //将Buffer中的数据写入到Channel中
    int bytesRead = inChannel.read(buf); //将数据从Channel中读取到Buffer里

    分散(Scatter)和聚集(Gather)

    分散读取是指从Channel中读取的数据“分散”到多个Buffer中

    ByteBuffer buf = ByteBuffer.allocate(128);
    ByteBuffer buf2 = ByteBuffer.allocate(1024);
    ByteBuffer [] bufs = { buf, buf2 };
    channel.read(bufs);

    注意: read方法按照ByteBuffer在数组中的顺序将Channel中的数据依次读取到Buffer中,在移动到下一个Buffer前,必须填满当前的Buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)

    聚集写入是指将多个Buffer中的数据“聚集”到一个Channel中

    ByteBuffer buf = ByteBuffer.allocate(128);
    ByteBuffer buf2 = ByteBuffer.allocate(1024);
    ByteBuffer [] bufs = { buf, buf2};
    channel.write(bufs);

    注意: write方法会按照Buffer在数组中的顺序依次将数据写入到Channel中,只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。因此与Scattering Reads相反,Gathering Writes能较好的处理动态消息。

    scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。

    transferFrom()

    FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中

    RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
    FileChannel fromChannel = fromFile.getChannel();
    RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
    FileChannel toChannel = toFile.getChannel();
    //定义传输位置
    long position = 0L;
    //最多传输的字节数
    long count = fromChannel.size();
    //将数据从源通道传输到另一个通道 toChannel.transferFrom(position, count, fromChannel);

    方法的输入参数position表示从position处开始向目标通道写入数据,count表示做多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。

    此外要注意,在SoketChannel的实现中,SocketChannel只会传输此刻准备好的数据(可能不足count字节)。因此,SocketChannel可能不会将请求的所有数据(count个字节)全部传输到FileChannel中。

    transferTo()

    FileChannel的transferTo()方法是将FileChannel里的数据传输到其他Channel中

    RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
    FileChannel fromChannel = fromFile.getChannel();
    RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
    FileChannel toChannel = toFile.getChannel();
    //定义传输位置
    long position = 0L;
    //最多传输的字节数
    long count = fromChannel.size();
    //将数据从源通道传输到另一个通道
    fromChannel.transferTo(position, count, toChannel);

    上面所说的关于SocketChannel的问题在transferTo()方法中同样存在。SocketChannel会一直传输数据直到目标buffer被填满。

  • 相关阅读:
    AJAX初识
    PE文件---导入表,导出表
    PE文件学习(基础)
    Android so(ELF)文件解析
    安卓加固方案从落地加载到类指令抽取编写报告
    安卓逆向从0到1学习总结
    DEX文件解析--7、类及其类数据解析(完结篇)
    网络设备配置--10、利用ACL配置访问控制
    网络设备配置--9、利用ppp协议实现点对点认证
    网络设备配置--8、利用ospf配置动态路由
  • 原文地址:https://www.cnblogs.com/yushangzuiyue/p/8460050.html
Copyright © 2011-2022 走看看