5、Channel
通道(Channel)用于源节点与目标节点的连接,在Java NIO中负责缓冲区中数据的传输,Channel本身不存储数据,因为需要配合缓冲区进行传输。Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
(1)、Channel接口实现类
①、FileChannel
从文件中读写数据。
②、SocketChannel
能通过TCP读写网络中的数据。
③、ServerSocketChannel
可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
④、DatagramChannel
能通过UDP读写网络中的数据。
(2)、获取Channel方式
①、getChannel()方法
Java针对支持通道的类提供了getChannel()方法。
本地IO:FileInputStream/FileOutputStream、RandomAccessFile
网络IO:Socket、ServerSocket、DatagramSocket
②、在JDK1.7中的NIO.2针对各个通道提供了静态方法open()
③、在JDK1.7中的NIO.2的Files工具类的newByteChannel()
(3)、AbstractSelectableChannel类
SelectableChannel抽象类,其子类:SocketChannel类、ServerSocketChannel类
public abstract class AbstractSelectableChannel extends SelectableChannel {
/**
* 注册一个选择器并设置监听事件,最后一个参数可以设置共享数据
*/
public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { ... }
/**
* 设置阻塞或非阻塞模式,取值false表示采用非阻塞模式
*/
public final SelectableChannel configureBlocking(boolean block) throws IOException { ... }
}
(4)、SocketChannel类
网络IO通道,具体负责进行读写操作。NIO把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
①、核心方法
public abstract class SocketChannel extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel {
/**
* 获取一个SocketChannel通道
*/
public static SocketChannel open() throws IOException { return SelectorProvider.provider().openSocketChannel(); }
/**
* 连接服务器
*/
public abstract boolean connect(SocketAddress remote) throws IOException;
/**
* 如果上面的方法连接失败,接下来就要通过该方法完成连接操作
*/
public abstract boolean finishConnect() throws IOException;
/**
* 从通道里读数据
*/
public abstract int read(ByteBuffer dst) throws IOException;
/**
* 往通道里写数据
*/
public abstract int write(ByteBuffer src) throws IOException;
}
(5)、ServerSocketChannel类
在服务器端监听新的Socket连接
①、核心方法
public abstract class ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel
{
/**
* 得到一个ServerSocketChannel对象,new ServerSocketChannelImpl()
*/
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
/**
* 设置服务器端端口号
*/
public final ServerSocketChannel bind(SocketAddress local) throws IOException
{
return bind(local, 0);
}
/**
* 接收一个连接,返回代表这个连接的通道对象
*/
public abstract SocketChannel accept() throws IOException;
}
(6)、FileChannel类
①、transferTo()方法
把数据从当前通道复制到目标通道。
/**
* 将字节从此通道的文件传输到给定的可写入字节通道。
* 尝试从该通道文件中的给定position开始读取最多count个字节,并将它们写入目标通道。 调用此方法可能会也可能不会传输所有请求的字节; 是否这样做取决于渠道的性质和状态。 如果此通道的文件包含从给定的position开始的少于count字节,或者目标通道是非阻塞且其输出缓冲区中的count字节少于position ,则传输的字节数少于所请求的字节数。
* @param position - 文件中的位置,从此位置开始传输;必须为非负数
* @param count - 要传输的最大字节数;必须为非负数
* @param target - 目标通道
* @return - 实际已传输的字节数,可能为零
*/
public abstract long transferTo(long position, long count, WritableByteChannel target);
②、transferFrom()方法
从目标通道中复制数据到当前通道。
/**
* 从给定的可读字节通道将字节传输到此通道的文件中。
* 尝试从源通道读取最多count个字节,并从给定的position开始将它们写入此通道的文件。 调用此方法可能会也可能不会传输所有请求的字节; 是否这样做取决于渠道的性质和状态。 如果源通道剩余少于count个字节,或者源通道非阻塞且输入缓冲区中立即可用的字节数少于count少于count字节数。
* @param src - 源通道
* @param position - 文件在转移开始时的位置; 必须是非负面的
* @param count - 要传输的最大字节数; 必须是非负面的
* @return
*/
public abstract long transferFrom(ReadableByteChannel src, long position, long count);
③、read(ByteBuffer dst)方法
从通道读取数据并放到缓冲区中
public abstract int read(ByteBuffer dst) throws IOException;
④、write(ByteBuffer src)方法
把缓冲区的数据写到通道中
public abstract int write(ByteBuffer src) throws IOException;
⑤、FileOutputStream、FileInputStream类,内部包含channel变量
public class FileInputStream extends InputStream {
private FileChannel channel = null;
}
public class FileOutputStream extends InputStream {
private FileChannel channel = null;
}
⑥、代码实现
/**
* 实现将1.jpg复制,命名为2.jpg
* 使用直接缓冲区完成文件的复制(内存映射文件)
*/
public void test1() throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
// 内存映射文件
MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
// 直接对缓冲区进行数据的读写操作
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst);
//关闭通道
inChannel.close();
outChannel.close();
}
/**
* 实现将1.jpg复制,命名为2.jpg
* 利用通道直接的数据传输(直接缓冲区)
*/
public void test2() throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//通道间数据传输
inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
//关闭通道
inChannel.close();
outChannel.close();
}