• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

java NIO的学习

武飞扬头像
whqObito
帮助5

Java NIOJava non-blocking IO 。 从jdk 1.4开始,Java提供了一系列改进的 I/O 的新特性,被统称为 NIO,是同步非阻塞的 .
NIO相关的类都被放在 java.nio包及子包下。

三大核心:

Channel(通道)、Buffer(缓冲区)、Selector(选择器)

Buffer :

四个属性:

capacity: 容量,表示缓冲区中最大存储数据的容量。一旦声明不能更改。
limit:界限,表示缓冲区中可以操作数据的大小。(limit 后的数据不能进行读写)
position: 位置,表示缓冲区中正在操作数据的位置。
mark: 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置。

学新通
学新通
学新通

测试:
public class test_Buffer {
    public static void main(String[] args) {
        CharBuffer charBuffer = CharBuffer.allocate(1024);
        String s="abcde";
        charBuffer.put(s.toCharArray());
        System.out.println(charBuffer.position());  // 5
        System.out.println(charBuffer.capacity());   // 1024
        System.out.println(charBuffer.limit());     //1024
        System.out.println("读写切换------------------------");
        charBuffer.flip();  //这里改成了读
        System.out.println(charBuffer.position());  //0 ,从第0个开始读起
        System.out.println(charBuffer.limit());     //5 ,只有前五个有内容
        char[] bytes= new char[charBuffer.limit()];
        charBuffer.get(bytes);
        System.out.println(new String(bytes));
        System.out.println(charBuffer.position());   //5 读到了第五个位置
        System.out.println(charBuffer.limit());     //5 只有前五个有内容
        charBuffer.clear(); //清除之后就默认变回 "写" ,因为根本不存在数据可以读
        System.out.println(charBuffer.position());   // 0
        System.out.println(charBuffer.limit());      //1024
    }
}
学新通

Channel :

通道(channel): Channel 表示 IO 源与目标打开的连接。Channel 类似于传统的流,只不过 Channel 本身不能直接访问数据,Channel 只能与 Buffer 进行交互。
主要实现类:
FileChannel(文件), SocketChannel(TCP源) ,ServerSocketChannel,DatagramChannel(UDP源)

Channel可以理解成原来的流 。

测试
public class test_channel {
    public static void main(String[] args) throws Exception {
//        writeChannel();
        readChannel();
    }
    public static ByteBuffer readChannel() throws IOException {
        FileInputStream inputStream=new FileInputStream(first.class.getClassLoader().getResource("a.txt").getPath());
        FileChannel channel = inputStream.getChannel();
        ByteBuffer byteBuffer= ByteBuffer.allocate(1024);
        channel.read(byteBuffer);  //从文件中读取内容
        byteBuffer.flip(); //切换为读
        System.out.println(byteBuffer.position());
        System.out.println(byteBuffer.limit());
        byte[] bytes=new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        System.out.println(new String(bytes));
        channel.close();
        inputStream.close();
        return byteBuffer;

    }

    public static void writeChannel() throws Exception {
        FileInputStream inputStream=new FileInputStream(first.class.getClassLoader().getResource("a.txt").getPath());
        FileChannel Rchannel = inputStream.getChannel();
        ByteBuffer byteBuffer= ByteBuffer.allocate(1024);
        Rchannel.read(byteBuffer);  //从文件中读取内容
        System.out.println(byteBuffer.position());
        System.out.println(byteBuffer.limit());
        System.out.println(byteBuffer.capacity());
        byteBuffer.put("新写入的内容".getBytes());
        System.out.println(byteBuffer.position());
        FileOutputStream outputStream=new FileOutputStream(first.class.getClassLoader().getResource("a.txt").getPath());
        FileChannel channel = outputStream.getChannel();
        byteBuffer.flip();  //读模式,channel根据 position 和limit将缓冲区的数据写入到文件
        System.out.println("读模式下的 position为" byteBuffer.position() ",limit为" byteBuffer.limit());
        channel.write(byteBuffer);
        channel.close();
        Rchannel.close();
        inputStream.close();
        outputStream.close();
    }
}
学新通

从上面的测试代码,我们可以发现,从输入流得到的Channel就只能将数据写到缓冲区,从输出流得到的Channel只能从缓冲区读出数据

Selector(仅网络编程Socket):

Java NIO 能实现非阻塞的 IO方式。就是使用了Selector ,可以用一个线程处理多个客户端的连接
当然,用selector实现非阻塞,channel本身也必须是非阻塞的,像文件的channel不能变成非阻塞,所以无法使用selector
Selector可以检测到多个注册的Channel 上是否有事件发生(多个 Channel以事件的方式可以注册到同一个 Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理,这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求。

注意,Selector 只有套接字可以用,也就是 ServerSocketChannel 才可以用

学新通

  1. 当客户端连接时,会通过 ServerSocketChannel 得到 SocketChannel。
  2. Selector 进行监听 select 方法,返回有事件发生的通道的个数。
  3. 将 socketChannel 注册到 Selector 上,register(Selector sel, int ops),一个 Selector 上可以注册多个 SocketChannel。
  4. 注册后返回一个 SelectionKey,会和该 Selector 关联(集合)。
  5. 进一步得到各个 SelectionKey(有事件发生)。
  6. 在通过 SelectionKey 反向获取 SocketChannel,方法 channel()。
  7. 可以通过得到的 channel,完成业务处理。
测试
public class test_Selector {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel channel1 =ServerSocketChannel.open();
        channel1.bind(new InetSocketAddress(8080));
        channel1.configureBlocking(false); //必须设置非阻塞才能使用selector
        SelectionKey key1 = channel1.register(selector, SelectionKey.OP_ACCEPT);
        ServerSocketChannel channel2 =ServerSocketChannel.open();
        channel2.bind(new InetSocketAddress(8081));
        channel2.configureBlocking(false); //必须设置非阻塞才能使用selector
        SelectionKey key2 = channel2.register(selector, SelectionKey.OP_ACCEPT);
        while(true){
            int select = selector.select();
            if(select>0){//说明有事件处理
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//取出每一个socket来查看
                while (iterator.hasNext()){
                    SelectionKey cur = iterator.next();
                    if(cur.isAcceptable()){ // 因为注册时使用 OP_ACCEPT 进行注册,所以只能判断是否有 accept事件
                        if(cur == key1){
                            System.out.println("8080端口有人连接");
                            SocketChannel accept = channel1.accept();
                            accept.configureBlocking(false);
                            accept.register(selector,SelectionKey.OP_READ); //注册读事件
                            System.out.println(accept.getRemoteAddress() "上线了");
                        }
                        if(cur == key2){
                            System.out.println("8081端口有人连接");
                        }
                    }else if(cur.isReadable()){ // 有channel注册读事件,那么此时就说明有通道是可读的
                        SocketChannel socketChannel = (SocketChannel) cur.channel();
                        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                        socketChannel.read(byteBuffer);
                        System.out.println("接收到数据了");
                        System.out.println(new String(byteBuffer.array()).trim());
                        byteBuffer.clear();
                        System.out.println(byteBuffer.limit());
                        System.out.println(byteBuffer.position());
                        socketChannel.write(ByteBuffer.wrap("服务器已经收到了".getBytes())); //注意发送消息时,缓冲区必须要使用wrap方法创建
                    }
                    iterator.remove();
                     //防止重复处理事件
                }
            }
        }
    }
}

学新通
public class client {
    public static void main(String[] args) throws IOException, InterruptedException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(8080));
        ByteBuffer byteBuffer = ByteBuffer.wrap("hello,server".getBytes()); //注意发送消息时,缓冲区必须要使用wrap方法创建
        System.out.println("客户端发送消息");
        socketChannel.write(byteBuffer);
        ByteBuffer recv = ByteBuffer.allocate(1024);
        socketChannel.read(recv);
        System.out.println("客户端收到:" new String(recv.array()).trim());
        Thread.sleep(50000);
        socketChannel.close();
    }
}

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhhhkg
系列文章
更多 icon
同类精品
更多 icon
继续加载