• nio系列(一)---nio重要组成


    nio重要组成部分

    前言:通过本文可以了解nio的重要组成部分,了解完基础的内容后后面理解才会简单一点。下一篇会讲讲nio的应用和io的对比。如果有不正确的地方还望指正。

    channel

    chanel接口的实现类

    • FileChannel 从文件中读取数据
    • DatagramChannel 通过udp读取网络中数据
    • SocketChannel 通过tcp读取网络中的数据
    • SeverSocketChannel 可以监听新进来的TCP连接,每一个新进来的连接都会创建一个SocketChanel

    通道的注意点

    • 通道可以是单向也可是双向的,同时实现ReadableByteChannel和WritableByteChannel接口可以实现双向运输
            RandomAccessFile aFile = new RandomAccessFile("C:\Users\jiajun\Desktop\Test.txt", "rw");
    		FileChannel inChannel = aFile.getChannel();
    		ByteBuffer buf=ByteBuffer.allocate(20);
    		inChannel.read(buf);
    		buf.flip();
    		inChannel.write(buf);
    		inChannel.close();
    	
    	//文件中本来有3个6,执行操作后有6个6
    
    • 通道可以阻塞也可以非阻塞,configureBlocking(boolean block)设置是否阻塞
    • 通道必须在缓冲区上操作,从缓冲区写入到通道或者通道写入到缓冲区

    Socket通道类

    • SocketChannel和ServerSocketChannel对应java.net包中的Socket和ServerSocket
    • 通道类的socket方法可以获取Socket对象

    打开socketChannel和ServerSocketChannel

    socketChannel socketChannel = SocketChannel.open();
    socketChannel.connect(new InetSocketAddress(ip, 80));
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
    

    非阻塞和阻塞

    • 在阻塞的情况下,accept方法会一直阻塞到有新连接到达
    
    
        SocketChannel socketChannel = serverSocketChannel.accept();
        //do something with socketChannel...
    
    
    • 在非阻塞情况下,accept方法会立即返回,如果还没有新的连接到达,返回null
    SocketChannel socketChannel =serverSocketChannel.accept();
    
            //do something with socketChannel...
    
        
    

    Buffer

    Buffer类的子类

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

    Buffer属性

    • 容量(capacity):所包含元素的数量,也就allocate分配的大小
    • 限制(limit):第一个不应该读取或写入的元素的索引,查看缓冲区中是否还有数据的时候会用到
    • 位置(position):下一个要读取或写入的元素的索引
    • 标志(mark):调用mark()来设置mark=position,再调用reset()可以让position恢复到标记的位置即position=mark

    重要方法

    • hasRemaining(),在缓冲区读取数据的时候用到,判断缓冲区是否还有数据
    public final boolean hasRemaining() {
            return position < limit;
        }
        
    
    • clear() 使缓冲区为一系列新的通道读取或相对放置 操作做好准备。它将限制设置为容量大小,将位置设置为 0。
    
    public final Buffer clear() {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
        }
    
    • flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。
    public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }
    
    • rewind():将position设回0,可以重读Buffer中的所有数据
    public final Buffer rewind() {
            position = 0;
            mark = -1;
            return this;
        }
    

    例子

    • 创建一个字符串数组,读写完一个字符串到缓冲区,就从缓冲区读取字符串
    public class TestMain
    {
        
        private static String[] strs = 
        {
            
            "jiajun",
            "jiajun6",
            "jiajun66",
            "jiajun666",
        };
        
        /**
         * 标识strs的下标索引
         */
        private static int index = 0;
        
        /**
         * 向Buffer内放置数据
         */
        private static boolean fillBuffer(CharBuffer buffer)
        {
            if (index >= strs.length)
                return false;
            
            String str = strs[index++];
            for (int i = 0; i < str.length(); i++)
            {
                buffer.put(str.charAt(i));
            }
            
            return true;
        }
        
        /**
         * 从Buffer内把数据拿出来
         */
        private static void drainBuffer(CharBuffer buffer)
        {
            while (buffer.hasRemaining())
            {
                System.out.print(buffer.get());
            }
            System.out.println("");
        }
        
        public static void main(String[] args)
        {
            CharBuffer cb = CharBuffer.allocate(100);
            while (fillBuffer(cb))
            {
                System.out.println("缓冲区的position:"+cb.position()+",缓冲区的limit:"+cb.limit()+",缓冲区的capacity:"+cb.capacity());
            	cb.flip();
                System.out.println("执行filp方法后缓冲区的position:"+cb.position()+",缓冲区的limit:"+cb.limit()+",缓冲区的capacity:"+cb.capacity());
    
                drainBuffer(cb);
                cb.clear();
                System.out.println("-----------------");
            }
        }
    }
    
    
    
    • 输出结果
    缓冲区的position:6,缓冲区的limit:100,缓冲区的capacity:100
    执行filp方法后缓冲区的position:0,缓冲区的limit:6,缓冲区的capacity:100
    jiajun
    -----------------
    缓冲区的position:7,缓冲区的limit:100,缓冲区的capacity:100
    执行filp方法后缓冲区的position:0,缓冲区的limit:7,缓冲区的capacity:100
    jiajun6
    -----------------
    缓冲区的position:8,缓冲区的limit:100,缓冲区的capacity:100
    执行filp方法后缓冲区的position:0,缓冲区的limit:8,缓冲区的capacity:100
    jiajun66
    -----------------
    缓冲区的position:9,缓冲区的limit:100,缓冲区的capacity:100
    执行filp方法后缓冲区的position:0,缓冲区的limit:9,缓冲区的capacity:100
    jiajun666
    -----------------
    

    Selector

    监听事件

    • Selector对监听的channel的什么事件感兴趣
    • SelectionKey.OP_CONNECT,channel成功连接到另一个服务器为连接就绪
    • SelectionKey.OP_ACCEPT,ServerSocketChannel准备好接受新进入的链接为接受就绪
    • SelectionKey.OP_READ,一个有数据可读的通道为读就绪
    • SelectionKey.OP_WRITE,等待写数据的通道为写就绪
     ServerSocketChannel ssc = ServerSocketChannel.open();
     ssc.register(selector, SelectionKey.OP_ACCEPT);
    //比如这里Selector对SeverSocketChannel的连接就绪感事件兴趣
    
    

    SelectionKey

    • 向Selector注册Channel时,regist方法会返回一个SelectionKey对象,代表了注册到Selector的通道
    public final SelectionKey register(Selector sel,int ops)throws ClosedChannelException
    
    • interest集合,所选择的感兴趣的事件集合,通过SelectionKey的interestOps()方法获取感兴趣事件结合,如果设置的为SelectionKey.OP_CONNECT,那么该方法返回SelectionKey.OP_CONNECT的值

    • selectionKey.isAcceptable() 检查channel是否接受就绪

    • selectionKey.isConnectable(),检查channel是否连接就绪

    • selectionKey.isReadable(),检查channel是否读就绪

    • selectionKey.isWritable(),检查channel是否写就绪

    select方法

    • 如果Selector注册的channel的感兴趣的事件还没有就绪,那么会阻塞
    • 当至少有一个channel的注册时间就绪的时候返回,返回的是自上次调用select()方法后有多少通道变成就绪状态
    • select(long timeout):执行选择,超过指定毫秒数则返回
    • selectNow():立刻执行选择,非阻塞,若没有已准备好的通道则立即返回0
    public abstract int select()throws IOException
    

    wakeUp方法

    • 当执行select方法后阻塞了,可以使用wakeup方法使阻塞在select方法的线程返回

    selectKeys方法

    • 返回此选择器已选择键集,通过该方法返回的集合来访问就绪的的通道
    • 注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。
    public abstract Set<SelectionKey> selectedKeys()
    

    我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

    作者:jiajun 出处: http://www.cnblogs.com/-new/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

  • 相关阅读:
    LeetCode 120:三角形最小路径和
    守护进程
    G711时间戳增量和数据包大小的关系
    H264防止竞争机制
    硬编码帧率错误导致的浏览器不能播放的问题
    GCC inline
    单例模式的双检锁的隐患和优化
    Java中异常捕获子类异常捕获在父类异常前面,即小范围先被捕获
    线程运行流程图
    将二维数组转为稀疏数组
  • 原文地址:https://www.cnblogs.com/-new/p/7382950.html
Copyright © 2020-2023  润新知