美文网首页
网络编程-NIO 理论部分

网络编程-NIO 理论部分

作者: 白璞1024 | 来源:发表于2020-02-16 12:07 被阅读0次

    NIO- no_block IO 或者叫NewIO JAVA 1.4引入的

    1 、NIO和BIO的区别

    1.1、面向流和面向缓存

    • IO是面向流的,没有缓存去,所以如果需要前后移动从流中读取的数据,需要先将他缓存到一个缓存区。
    • NIO是面向缓存区

    1.2、阻塞与非阻塞

    • java 的IO是阻塞模式的,当一个线程调用read()或者是write的时候,线程会被阻塞,
    • NIO非阻塞模式,是一个线程从某个通道发送请求读取数据,但是仅仅能得到目前可用的数据。如果目前没有可用的数据的时候,就什么都不会获取,而不是保持线性阻塞,没有获取到想要的信息的时候可以干其他的事情。所以一个单独的线程可以管理多个输入和输出的通道。

    1.3、Select

    • NIO允许一个单独的线程来监视多个输入通道。可以注册多个通道使用一个选择器,然后使用一个单独的线程来选择通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

    2、selector

    selector选择器,轮训代理器,事件订阅器,channel容器管理器

    应用程序将select对象注册需要它关注的Channel,以及具体的某一个Channel会对哪些IO事件感兴趣,Selector中也会维护一个已经注册的Channel的容器。

    3、Channels

    通道,被建立的一个应用程序和操作系统交互事件,传递内容的渠道,注意是连接到操作系统的 程序可以通过通道读取数据,也可以通过通道向操作系统写数据,而且可以同时进行读写。

    • 所有被select注册的通道,只能是继承了selectableChanne l类的子类
    • ServerSocketChannel应用服务器程序的兼听监听通道。只有通过这个通道,程序才能向操组系统注册支持’多路复用IO的端口监听。同时支持UDP,TCP
    • SocketChannel TCPSocket套接字的监听通道,一个Socekt对应了一个客户端的IP,端口到服务器IP端口的通信连接

    通道中的数据总是要先读到一个Buffer或者,总是要从一个Buffer中写入

    4、SelectionKey

    select对象注册感兴趣的事件的时候,JAVA NIO共定义了四种 OP_READ、OP_WRITE、OP_CONNECT、 OP_ACCEPT

    • 服务器启动ServerSocketChannel关注OP_ACCEPT事件
    • 客户端启动SocketChannel连接服务器,关注OP_CONNECT事件
    • 服务器接受连接,启动一个服务器的SocketChannel,这个SocketChannel可以关注OP_READ OP_WRITE事件,一般连接后会直接关注OP_READ事件
    • 客户端这边的客户端SOCKETCHANNEL发现连接建立之后,可以关注OP_READ OP_WRITE事件,一般需要客户端发送数据了才会关注OP_READ事件

    四、Buffer

    用来和NIO通道进行交互,数据是从通道读入缓存区,缓存去写入到通道中的。以写为例:应用程序都是将数据写入缓存,在通过通道把数据发出去,读也是一样的。缓存本质上就是一个可以写入数据,然后可以读取数据的内存。这块内存被包装成NIO Buffer的对象,提供了一组方法,用来方便的访问这块内存。

    1、重要属性

    1. capacity

    内存卡,Buffer又一个固定的大小,就是capacity,一旦满了,需要清空(读数据,清除数据) 才能继续往里写数据。

    1. position

      当你写数据到Buffer中的时候,position表示当前的位置,初始position值是0.当一个byte, long,等数据写到Buffer后,position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity-1。读取数据的时候,也是也是从一个特定的位置开始读,Buffer写模式换成读模式的时候,position会重置成0档从Buffer的postion处读取数据的时候,position向前移动到下一个可读到位置。

    2. limit

      写模式下,Buffer的limit表示你最多能往Buffer里写多少的数据。写模式下,limit等于Buffer的capacity

      读模式下,Buffer到读模式的时候,limit表示你最多能读多少数据。切换Buffer到读模式的时候,limit会被设置成写模式的下的position值。你能读到之前写入的所有的数据。

    2、Buffer的分配

    想要活的一个Buffer对象首先要进行分配,每一个Buffer类都有allocate方法,可以在堆上分配,也可以直接内存上分配。

    ByteBuffer buf = ByteByffer.allocate(48);//分配48字节的
    CharBuffer buf = CharBuffer.allocate(1024);
    
    

    把一个byte数组或者是byte数组的一部分包装成ByteBuffer

    ByteBuffer wrap(byte[] array)
    ByteBuffer wrap(butep[] array,int office,int length); 
    

    HeapByteBufferDirectByteBuffer原理上,前者可以看出分配的buffer是在heap区域的,其实真正的flush到远程的时候,会先复制到直接内存,再做下一步操作。

    NIO框架下,很多框架会采用DirectByteBuffer来操作,这样内存不再是heap上,而是操作在系统的C heap上,经过性能测试,可以得到非常快的网络交互。大量的网络交互下,一般速度会比HeapByteBuffer快速好几倍。

    直接内存(Di re c t M e me o r y)并不是虚拟机运行时候数据区的一部分,也不是java虚拟机规范中定义的内存区域。这部分内存频繁的被使用,也可以导致OurOfMemoryError出现。NIO可以使用Native函数库直接分配对外内存,然后通过一个存储在java堆里边的DirectByteBuffer对象作为这个内存的应用进行处理

    >1 堆外内存优缺点

    相比较堆内内存有几个优势:

    1. 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作。
    2. 加快了复制的速度。省略了堆内flush到远程的时候,先复制到直接内存的过程;

    不好的一面

    1. 难以控制,如果内存泄露,很难排查
    2. 不适合存储很复杂的对象,一般简单的对象或者是扁平化的比较合适。

    >2 直接内存(堆外内存)与堆内存的比较

    ​ 直接内存申请空间耗费更多的性能。频繁处理的时候这一点很明显

    ​ 直接内存IO速度性能优于普通内存。多次读写操作的情况下,差异明显

    3、Buffer的读写

    1. 向Buffer中写数据

    两种方式:

    • 读取Channel写到Buffer

      int bytesRead = inChannel.read(buf);//read into buffer
      
    • 通过Buffer的put()方法到Buffer

      buf.put(127);
      flip()方法:
      flip方法将Buffer从写模式切换到读模式,调用flip会将position改为0并将limit,设置成之前position的值。

    2. 从Buffer中读取数据

    • 从Buffer读取数据写入到Channel

    • 使用get方法从Buffer中读取数据。

      int bytesWritten = inChannel.write(buf);//从Buffer读取数据到Channel
      bute aByte =buf.get();//从Buffer读取数据到Channel
      

    使用Buffer读写数据常见步骤

    1. 写入数据到Buffer
    2. 调用flip()方法
    3. Buffer中读取数据
    4. 调用clear()方法或者 compact()方法,清除掉缓存中的数据。
    • clear()方法:position将被设回0 limit被设置成capacity的值,换句话说Buffer被清空了,Buffer中的数据并未清除。如果Buffer中又一些未读的数据,调用clear()方法,数据将被遗忘,以为不再有任何标记会告诉你哪些数据被读过,哪些还没有。

      如果Buffer中仍然有未读的数据,且后续还需要这些数据,但是此时想要先写些数据,那么使用conpact方法。

    • compact()方法将所有未读的数据拷贝到Buffer起始处,,然后将position设置到最后一个未读元素正后面,limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好些数据了,但是不会覆盖未读的数据。

    • equals()与compareTo()方法:比较两个Buffer

    • equals满足下列条件是,表示两个Buffer相等:

      • 有相同的类型(byte char int)
      • Buffer中剩余的byte,char等的个数
      • Buffer中所有剩余的byte,char等都相同。
    • compareTo()方法:

      compareTo方法比较两个Buffer的剩余元素,如果满足下列条件,认为一个Buffer小于另一个Buffer

      1. 第一个不相等的元素小于另一个Buffer中对应的元素。
      2. 所有元素都相等,但第一个Buffer比另一个先耗尽,第一个Buffer的元素个数比另一个少。

    相关文章

      网友评论

          本文标题:网络编程-NIO 理论部分

          本文链接:https://www.haomeiwen.com/subject/tqspfhtx.html