Java NIO 基础

1. NIO 介绍

NIO(non-blocking io):非阻塞IO,JDK1.4 引入。

2. NIO 三大组件

2.1 Channel

Channer是读写数据的双向通道,类似于传统IO中的Stream,但是Stream只能单向操作。如:InputStream只能读操作,OutputStream只能写操作。

2.1.1 常见的 Channel

  • FileChannel:文件IO通道,用于文件的读写
  • DatagramChannel:UDP协议数据报通信
  • SocketChannel:网络套接字IO通道,TCP协议客户端
  • ServerSocketChannel:网络套接字IO通道,TCP协议服务端

2.1.2 常用方法

  • read(ByteBuffer buffer):从Channel中读取到ByteBuffer中,如果Channel中没有数据会一直堵塞到可读
  • read(ByteBuffer buffer,Long timeout):从Channel中读取到ByteBuffer中,超过时间会报错
  • write(ByteBuffer buffer):将数据写到Channel中,如果Channel中没有可写空间会一直堵塞到可写
  • write(ByteBuffer buffer, Long timeout):将数据写到Channel中,超过时间会报错
  • flush():将Channel中缓冲区数据刷到底层设备
  • register(Selector selector, SelectionKey key):将Channel注册到Selector
  • configureBlocking(boolean b):设置Channel是否为阻塞模式
  • socket():获取底层的Socket对象
  • isConnected():Channel是否已经连接上
  • isReadable():Channel是否可读
  • isWriteable():Channel是否可写
  • getRemoteAddress():Channel对应的远程地址
  • getLocalAddress():Channel对应的本地地址
  • open():Channel是否打开

2.2 Buffer

Buffer:缓冲读写数据,每一个Buffer对象关联一个字节数组。
在这里插入图片描述

2.2.1 常见的 Buffer

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

2.2.2 重要属性

  • capacity:Buffer所占的内存大小,设置后不能修改
  • limit:Buffer中可以操作的数据大小
  • position:下一个要读/写的数据索引
  • mark:标记当前position的位置,可以通过reset()position恢复到mark位置

2.2.3 常用方法

  • capacity():返回capacity的值
  • limit():返回limit的值
  • limit(int n):设置limit的值
  • position():返回position的值
  • position(int n): 设置position的值
  • mark():对Buffer做标记
  • reset():把position恢复到mark位置
  • rewind():设置position为0,取消mark标记
  • hasRemaining():判断Buffer中是否有元素
  • get():从Buffer中读取一个字节
  • get(byte[] b):从Buffer中读取多个字节
  • get(int index):从Buffer中读取指定索引位的字节
  • put(byte b):往Buffer中存一个字节
  • put(byte[] b):往Buffer中存多个字节
  • put(int index, byte b):往Buffer指定索引位存字节
  • clear():清空Buffer
  • compact():清空position之前的字节
  • flip():将limit设置为position的值,position设置为0

2.3 Selector

Selector配合一个线程管理多个Channel,获取Channel上发生的事件

2.3.1 四种事件类型

  • OP_CONNECT:连接成功后(只客户端使用)
  • OP_ACCEPT:客户端请求连接时(只服务端使用)
  • OP_READ:读缓冲区有可读数据时
  • OP_WRITE:写缓冲区有可写空间时
    在这里插入图片描述
package com.learn.wesay;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

public class Server {
    public static void main(String[] args) throws IOException {
        // 创建Selector管理多个Channel
        Selector selector = Selector.open();

        // 创建服务端套接字通道
        ServerSocketChannel server = ServerSocketChannel.open();
        // 设置为非阻塞
        server.configureBlocking(false);
        // 绑定端口
        server.bind(new InetSocketAddress(8888));

        // Channel注册到Selector,只处理accept事件
        server.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 没有事件发生线程阻塞,有事件线程才会恢复运行
            selector.select();
            // 所有发生的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    handlerAccept(selectionKey, selector);
                }

                if (selectionKey.isReadable()) {
                    handlerRead(selectionKey, selector);
                }

                iterator.remove();
            }
        }

    }

    private static void handlerAccept(SelectionKey selectionKey, Selector selector) {
        ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
        try {
            SocketChannel channel = server.accept();
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ);
            System.out.println(channel);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void handlerRead(SelectionKey selectionKey, Selector selector) {
        SocketChannel channel = (SocketChannel) selectionKey.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        try {
            int read = channel.read(byteBuffer);
            if (read == -1) {
                // 客户端socket正常断开
                selectionKey.cancel();
            } else {
                byteBuffer.flip();
                System.out.println(StandardCharsets.UTF_8.decode(byteBuffer));
            }
        } catch (IOException e) {
            // 客户端socket异常断开
            selectionKey.cancel();
        }
    }
}

public class Client {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 8888));
        socketChannel.write(Charset.defaultCharset().encode("你好"));
    }
}
  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值