Java NIO

字节缓冲区

字节顺序-大端-小端

ByteOrder.nativeOrder().toString() 获取当前处理器的字节顺序

直接缓冲区

ByteBuffer.allocateDirect() 创建所需容量的直接缓冲区

isDirect() 是否直接缓冲区

只有字节类型数据才可以创建直接缓冲区,其他类型可使用视图缓冲区

ByteBuffer.asCharBuffer() 根据字节直接缓冲区创建一个字符直接缓冲区

视图缓冲区

与源缓冲区操作地址一致,但position\limit 等标识可不一致。

byteBuffer.as***() 此类操作以字节顺序包装为指定缓冲区

duplicate()

slice()

通道

通道是途径,是抽象的概念,受控的且可移值的方式来访问操作系统底层I/O

  • 文件通道
  • Socket通道

阻塞、非阻塞

打开通道

FileChannel.getChannle()

SocketSchannel.getChannel()

关闭通道

通道是无法重复使用的

中断阻塞在通道上的线程会自动关闭通道

channel.close(); 在网络上可能是阻塞的(在内容被提取前,部分框架可能会阻塞通道关闭)

Scatter/Gather(发散/收集)

以Channel为视角,channel.writer(Buffer[] ) 。channel.read(Buffer[])

通道接收多个缓冲区,将多个缓冲区内容“Gather”到通道。

将通道内容“Scatter”到多个缓冲区。

这个过程是顺序的。也是高效的(由操作系统直接完成)。

buffer--\         channel            / buffer
buffer----Scatter =======  Gather------buffer
buffer--/                            \ buffer

文件通道

FileChannel

read()/write()方法会改变Position位置,同一文件的多个对象是共享Position的(由操作系统控制)。

read(p)/write(p) 这个是不共享的。

将position位置移动到大于文件size的位置,调用write()可能会照成 "文件空洞"。

truncate() 会去除超出的size值,并将position = size

force(boolean) 类似刷盘的操作,强制将文件内容写入磁盘。参数标识是否将文件元数据强制写入。

文件锁

不支持共享锁的操作系统自动升级到独享锁。

锁的对象是文件,而不是通道或者缓冲区。

锁判断的是进程而不是线程,意味着同一Java进程获取到锁后同一进程都能获取到锁。

FileChannel.lock(position,size, shared) 锁定position-size位置,shared标识是否共享。

channel关闭、FileLock.release()、进程关闭会释放锁。

FileLock.isVaild() 校验锁是否有效。

避免死锁。

内存映射文件

MapperByteBuffer 将物理磁盘中的文件直接在程序中操作。

load()

isLoaded()

force() 与FileChannel.force()一样


transferto/transferForm 通道间文件数据传输,有一端必须为FileChannel。传输当前通道已有数据到另一通道。不经过用户空间。

Socket通道

ServerSocketChannel

  • open()
  • socket()
  • accept()
  • vaildOps()

SocketChannel

    @Test
    public void ser() throws Exception {
        SocketChannel socketChannel = SocketChannel.open();
        // 设置非阻塞模式
        socketChannel.configureBlocking(false);
        // 链接
        // 链接一旦被打开只有结束才可以关闭
        // 非阻塞模式下调用 connect 会立即返回信息,如果无法立即建立链接则返回false并继续建立链接
        System.out.println("start.....");
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 3306));

        /**
         * finishConnect() 操作可能触发的事情
         * connect( )方法尚未被调用。那么将产生 NoConnectionPendingException 异常。 􏰈连接建立过程正在进行,尚未完成。那么什么都不会发生,finishConnect( )方法会立即返回
         * false 值。
         * 􏰈在非阻􏰅模式下调用connect( )方法之后,SocketChannel又被切换回了阻􏰅模式。那么如果
         * 有必要的话,调用线程会阻􏰅直到连接建立完成,finishConnect( )方法接着就会返回true
         * 值。
         * 在初次调用 connect( )或最后一次调用 finishConnect( )之后,连接建立过程已经完成。那么
         * SocketChannel 对象的内部状态将被更新到已连接状态,finishConnect( )方法会返回 true
         * 值,然后 SocketChannel 对象就可以被用来传输数据了。
         * 连接已经建立。那么什么都不会发生,finishConnect( )方法会返回 true 值
         */
        // 链接未建立或建立中阻塞任何读写操作
        // 方法用作完成链接过程
        // 链接被拒绝抛出 java.net.ConnectException: Connection refused
        while (!socketChannel.finishConnect()) {
            // 链接还未建立成功
            System.out.println("loading.....");
        }

        if (socketChannel.isConnected()) {
            // 链接还建立成功
        }
        // 链接建立成功
        System.out.println("success");
    }

DatagramChannel

基于UDP/IP协议,一个DatagramChannel可以即是服务端也可以是客户端,且是可以重复使用的。

 @Test
    public void datagramServer() throws Exception {
        DatagramChannel datagramChannel = DatagramChannel.open();
        // datagramChannel.configureBlocking(false);
        datagramChannel.socket().bind(new InetSocketAddress(9090));

        SocketAddress socketAddress = null;
        ByteBuffer allocate = ByteBuffer.allocate(10);
        allocate.putChar('H');
        while (true) {
            // 在非阻塞模式下 未接收到消息 返回将为null
            // 数据包 可以接受任意链接
            socketAddress = datagramChannel.receive(allocate);
            if (socketAddress == null) {
                // 未接收到消息
                continue;
            }
            // socketAddress 对象值为消息的来源
            allocate.flip();
            System.out.println(new String(allocate.array(), StandardCharsets.UTF_8));
            break;
        }

        if (datagramChannel.isConnected()) {
            // datagram与socketChannel不同,datagram可以重复使用 所以提供了disconnect()方法
            // 已链接的datagram可以任意使用通道 read/write
            System.out.println("connected");
        }

        // 给个回复
        datagramChannel.send(ByteBuffer.wrap("hello".getBytes(StandardCharsets.UTF_8)), socketAddress);

    }

    @Test
    public void datagramClient() throws Exception {
        DatagramChannel datagramChannel = DatagramChannel.open();
        // datagram允许不绑定,open()方法在打开一个底层的socket时分配了一个动态端口
        // 未绑定的datagram依然可以接受与发送数据包,这个分配的端口是由操作系统决定的

        // 发送
        datagramChannel.send(ByteBuffer.wrap("hello world".getBytes(StandardCharsets.UTF_8)), new InetSocketAddress("127.0.0.1", 9090));

        ByteBuffer allocate = ByteBuffer.allocate(10);
        // 这里可以看看有没有回复
        SocketAddress socketAddress = datagramChannel.receive(allocate);
        if (socketAddress != null) {
            allocate.flip();
            System.out.println(new String(allocate.array(), StandardCharsets.UTF_8));
        }
    }
posted @ 2022-05-11 19:18  ccme  阅读(45)  评论(0)    收藏  举报