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();
- }