Java NIO — 通道(Channel)

NIO 中主要的三个概念为缓冲区、通道、选择器,它们之间的关系如下所示:

此处要提醒的是,JDK 1.7 升级了 NIO 类库,升级后的 NIO 类库被称为 NIO2.0。在 NIO2.0 中,提供了异步文件I/O操作,同时提供了与 UNIX 网络编程事件驱动I/O对应的 AIO。

在之前《Java NIO 缓冲区》一文中已经介绍过缓冲区的相关知识,本文主要介绍通道的使用。


通道(Channel)

Channel 用于缓冲区与文件或套接字之间有效的传输数据。传统的字符/字节流不同的读写操作是分开的,对应于 InputStream 与 OutputStream 两个类,而 Channel 同时支持读写操作,可以更好的映射底层操作系统的API。

Channel 在 Java 中是一个接口,里面只有两个方法,用来检查通道是否打开与关闭通道:

public interface Channel extends Closeable {

    public boolean isOpen();

    public void close() throws IOException;

}

Channel接口与其主要实现类的结构图如下所示:

  • ReadableByteChannel:支持读取字节的通道
  • WritableByteChannel:支持写入字节的通道
  • NetworkChannel:NIO 2.0 新增,用于加强对 Socket 的操作
  • SelectableChannel:支持通过 Selector 实现多路复用的通道

可以看到,FileChannel、SocketChannel、DatagramChannel 都实现了 ReadableByteChannel 与 WritableByteChannel 接口,因此它们都同时支持读写操作。

此外,对于图中的四种Channel来说,它们都是线程安全的。

  • FileChannel:文件通道,可以通过在 RandomAccessFile、FileInputStream、FileOutputStream 对象上调用 getChannel() 方法获取,在NIO 2.0 中可以通过 open() 方法直接打开文件创建文件通道
  • SocketChannel:通过静态 open() 方法创建。对应 Socket,通过 socket() 方法获得与之关联的 socket 对象
  • ServerSocketChannel:通过静态 open() 方法创建。对应 ServerSocket,通过 accept() 方法接收请求,并返回 SocketChannel 对象
  • DatagramChannel:通过静态 open() 方法创建。对应 DatagramSocket,用于 UDP 数据包的传输

通道可以设置为阻塞或非阻塞模式,默认是阻塞模式,可以通过 configureBlocking(false) 方法设置为非阻塞模式。

要注意的是,FileChannel 只能运行在阻塞模式下,其它通道可以在阻塞和非阻塞模式之间选择。通道需要和字节缓冲区配合使用。

ServerSocketChannel(服务器端)示例:

public class SSCDemo {

    public static void run() throws IOException, InterruptedException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.bind(new InetSocketAddress(1212));
        while (true) {
            System.out.println("正在等待连接...");
            SocketChannel sc = ssc.accept();
            if (sc == null)
                TimeUnit.SECONDS.sleep(2);
            else {
                System.out.println("新连接接入:" + sc.getRemoteAddress());
                sc.read(byteBuffer);
                byteBuffer.flip();
                System.out.print("接收数据:");
                while (byteBuffer.hasRemaining()) {
                    System.out.print((char) byteBuffer.get());
                }
                System.out.println();
                byteBuffer.clear();
            }
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        SSCDemo.run();
    }
}

SocketChannel(客户端)示例:

public class SCDemo {

    public static void run() throws IOException, InterruptedException {
        byte[] array = "Hello World!".getBytes();
        ByteBuffer byteBuffer = ByteBuffer.wrap(array);
        SocketChannel sc = SocketChannel.open();
        sc.configureBlocking(false);
        sc.bind(new InetSocketAddress(1213));
        sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), 1212));
        while (!sc.finishConnect()) {
            System.out.println("连接中...");
            TimeUnit.SECONDS.sleep(2);
        }
        System.out.println("连接完成!");
        sc.write(byteBuffer);
        sc.close();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        SCDemo.run();
    }
}

运行结果(服务器端):

正在等待连接...
正在等待连接...
正在等待连接...
新连接接入:/169.254.77.64:1213
接收数据:Hello World!
正在等待连接...
正在等待连接...

猜你喜欢

转载自blog.csdn.net/weixin_43320847/article/details/83240643