channel
Channel是仅次于Buffer的重要模块, Channel是IO设备传输的媒介.
通常来说, 操作系统的每个文件标识符都会有相应的channel对应. Channel类不仅提供了平台无关性的抽象, 并且同样支持现代操作系统的本地io能力.
Channel提供了使用操作系统的本地io的服务, 并且只消耗很小. Buffer就是传输过程数据的容器.
Java NIO Channel
Channel接口提供了两个方法如下
public interface Channel extends Closeable {
public boolean isOpen();
public void close() throws IOException;
}
isOpen()用于判断channel是否打开;
close()用于关闭一个打开的channel.
如何打开一个channel
io操作一共就两种, 文件io和网络io. 所以也就有两大类channel: FileChannel和SocketChannel.
下面通过几个例子来看下如何创建Channel.
创建FileChannel
FileChannel只能通过文件流使用getChannel()方法获取. 文件流指的是RandomAccessFile, FileInputStream, FileOutputStream. FileChannel是不能直接创建的.
RandomAccessFile raf = new RandomAccessFile ("somefile", "r");
FileChannel fc = raf.getChannel();
创建SocketChannel
与FileChannel不同, SocketChannel有可以创建的工厂方法.
//SocketChannel
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("somehost", someport));
//ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind (new InetSocketAddress (somelocalport));
//DatagramChannel
DatagramChannel dc = DatagramChannel.open();
以上方法都会创建一个channel对象, 与FileChannel不同的是, SocketChannel如果已经存在的话是不会创建一个新的.
如何使用Channel
可以看到Channel接口只提供了两个方法, 没有提供关于读取的方法, 那么channel读取的功能就需要子类来实现. 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;
}
从名字就能知道每个子类的功能. 如果同时想操作读取则可以直接实现ByteChannel
public interface ByteChannel extends ReadableByteChannel, WritableByteChannel{
}
对, 没看错, 里面啥也没有, 该接口设计的目的应该就是为了不用每次都实现两个接口.
继承关系如图
上面说的FileChannel和SocketChannel都继承了ByteChannel, 所以都可以执行读写. 聪明如你看出问题了么?
对, 对于文件流来说, 通过FileInputStream获取到的FileChannel是"只读"的, 所以此时执行写操作会抛出异常.
ByteChannel的read()和write()方法的参数是ByteBuffer. 两个方法的返回参数是int, 表示已经传输的字节数量. 返回值可以小于buffer中的字节数, 甚至可以是0. buffer中的position也会向前移动的一样的距离.
如果一个buffer只传输了一部分数据, 那么我们还可以继续提交buffer给channel继续传输剩下的部分. 重复上述步骤, 直到hasRemaining()方法返回false.
下面的例子是从一个channel复制数据到另一个channel
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
public class ChannelCopyExample {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("in.txt");
ReadableByteChannel readableByteChannel = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("out.txt");
WritableByteChannel writableByteChannel = fileOutputStream.getChannel();
copyData(readableByteChannel, writableByteChannel);
readableByteChannel.close();
writableByteChannel.close();
}
private static void copyData(ReadableByteChannel readableByteChannel, WritableByteChannel writableByteChannel) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1 * 1024);
while (readableByteChannel.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
writableByteChannel.write(buffer);
}
buffer.clear();
}
}
}
channel有两种工作模式: 阻塞模式和非阻塞模式. 当channel在非阻塞模式下时, channel不会将工作线程设置为睡眠状态. 而且请求操作的响应会立即返回, 可能是处理完了, 也可能是没有处理完. 也就是说在非阻塞模式下, 请求会马上得到结果不会阻塞主工作线程. 只有流式的channel才可以设置为非阻塞状态, 比如socket和pipe.
channel的关闭
关闭channel只需要调用close()方法, 与buffer不同的是当channel关掉后就不能再重复使用了. 一个打开的channel代表了和某个io服务的连接, 而且还封装连接的状态信息. 当channel关闭时, 连接也就丢失了, 此时channel不再连接到任何io设备.
我们可以通过isOpen()方法判断当前channel是否关闭, true表示打开, false表示关闭. 当对一个关闭channel读写或者其他需要channel打开的操作都会抛出异常.
参考链接 https://howtodoinjava.com/java/nio/java-nio-2-0-channels/