网络编程15

JDK中nio实现

ServerSocketChannel的继承关系

  • channel接口

  • InterruptibleChannel接口

    • 是可以中断的
  • NetworkChannel接口

    • 与网络通信有关
  • AbstractInterruptibleChannel抽象类

    • 对InterruptibleChannel接口的实现
  • SelectableChannel抽象类

  • AbstractSelectableChannel抽象类

  • ServerSocketChannel抽象类

SocketChannel的继承关系

  • SocketChannel的继承关系右边部分与ServerSocketChannel相同,多出来一部分读写Channel,即从channel又多出来两个分支
  • WritableByteChannel接口
  • ReadableByteChannel接口
  • GatheringByteChannel接口
  • ByteChannel接口
  • ScatteringByteChannel接口

AbstractInterruptibleChannel—上述两者的第一个实现类

  • 可异步、可中断的channel
    • 可异步关闭:有一个线程阻塞在当前channel某个操作上,另一个线程进来调用了这个channel的close方法,阻塞的线程就会抛出一个异步关闭的异常
    • 可中断:被阻塞的线程,另一个线程执行阻塞线程的interrupt方法,阻塞线程就会抛出线程中断异常
  • interrupt方法,只是把标志位有false改为true,并不会真正的中断线程,是否中断线程由当前线程确定
  • 调用iterrupt方法的时候,利用Thread.blockedOn方法传递一个方法,来做channel.close这件事,调用blockedOn方法时传入参数是Interruptible,这是一个接口

blockedOn

  • Thread线程里面也有这个方法

begin

  • interruptor对象为空,会初始化这个对象,这个初始化方法里面实现了关闭channel,同时指定了需要中断的线程

  • 然后执行blockedOn方法

  • Selector中的中断也是采用这种模式

end

  • 如果判定当前线程要中断了,抛出两个异常
    • ClosedByInterruptException
    • AsynchronousCloseException

begin方法要和end方法组合起来使用

  • try{
          
          
    	begin();
    }finally{
          
          
    	end();
    }
    
  • 为阻塞的线程实现了异步中断和关闭

SelectableChannel

  • 在AbstractInterruptibleChannel的基础上,增加了selectorkey到selector上的注册

  • register方法声明和定义

AbstractSelectableChannel

  • 实现了selectorKey的注册

register

  • 进行各种检查

    • channel是否打开
    • 是否阻塞,所以在写handler时要把ServerSocketChannel设置成非阻塞,不然会抛出异常
  • findKey

    • 轮询keys数组,是否有当前key
    • 如果有,则更新这个key的操作,同时传入新的属性参数
    • 如果不存在,则进行注册,并添加到keys数组中

其他方法

  • 都是围绕selectorKey进行的相关操作
    • 数组扩容
    • key移除

SocketChannel

  • 也是一个抽象类,主要是对底层socket访问的抽象
    • bind
    • connect
  • 非抽象方法
    • open

open

  • SelectorProvider.provider().openSocketChannel()
    

SelectorProviderImpl–SelectorProvider

  • 返回socketChannel的实现

SocketChannelImpl

  • 定义了socketChannel的生命周期的状态参数值

    • 初始化完成变成未连接
    • 调用connect方法,调用成功变成已连接,如果是非阻塞式的调用,变成pending_connect(等待连接)
  • connect

    • 抛开各种检查,最终底层还是交给socket的原生connect0方法

    • 支持可异步中断、可异步关闭的方法,begin–end的组合方法使用

ServerSocketChannel

  • 抽象方法

    • bind
    • accept
  • 非抽象方法

    • open

open

  • 与SocketChannel是类似的

ServerSocketChannelImpl

  • 与服务器通讯相关

  • bind

    • Net.bind执行绑定方法

    • Net.listen执行监听方法

  • accept

    • 可异步中断

Selector

  • public abstract class Selector implements Closeable
    
  • 本质是一个抽象类

  • 服务器和客户端的通讯是不定时的,虽然支持百万连接,但是大部分都是空闲的,这就是为什么提出io多路复用模型

  • selecor的实现是与操作系统相关的

    • windows
    • linux

Selector.open

  • 跟channel方法一样

SelectorProviderImpl

  • openSelector仍然是一个抽象方法

DefaultSelectorProvider

  • 缺省实现

  • create

    • 如果是sun公司,DevpollSelectorProvider

    • 如果是linux,EpollSelectorProvider

      EpollSelectorImpl

jdk会根据操作系统类型返回该操作系统的provider,再返回对应操作系统的SelectorImpl

SelectorImpl的继承关系

  • AutoCloseable接口
  • Closeable接口
  • Selector抽象类
  • AbstractSelector抽象类

SelectorImpl

  • SelectorImpl实例化

    • 实例化两个key集合

      publicKeys存放所有key

      publicSelectedKeys存放已经就绪的key

  • register-----channel的注册方法,最终调用链的最后还是由selector实现的

    • @Override
      protected final SelectionKey register(AbstractSelectableChannel ch,
                                            int ops,
                                            Object attachment)
      {
              
              
          if (!(ch instanceof SelChImpl))
              throw new IllegalSelectorException();
          SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
          if (attachment != null)
              k.attach(attachment);
      
          // register (if needed) before adding to key set
          implRegister(k);
      
          // add to the selector's key set, removing it immediately if the selector
          // is closed. The key is not in the channel's key set at this point but
          // it may be observed by a thread iterating over the selector's key set.
          keys.add(k);
          try {
              
              
              k.interestOps(ops);
          } catch (ClosedSelectorException e) {
              
              
              assert ch.keyFor(this) == null;
              keys.remove(k);
              k.cancel();
              throw e;
          }
          return k;
      }
      

      1.首先包装成SelectionKeyImpl

      2.implRegister是一个抽象方法,又需要跳到EPollSelectorImpl的实现去看

  • select

    • 最终都是调用lockAndDoSelect这个方法
  • lockAndDoSelect

    • private int lockAndDoSelect(Consumer<SelectionKey> action, long timeout)
          throws IOException
      {
              
              
          synchronized (this) {
              
              
              ensureOpen();
              if (inSelect)
                  throw new IllegalStateException("select in progress");
              inSelect = true;
              try {
              
              
                  synchronized (publicSelectedKeys) {
              
              
                      return doSelect(action, timeout);
                  }
              } finally {
              
              
                  inSelect = false;
              }
          }
      }
      
    • 又调用doSelect方法,又要进入EPollSelectorImpl中

EpollSelectorProvider

  • linux下Selector.open返回的

EPollSelectorImpl—直接与操作系统交互

fd0和fd1

  • fd0
    fd1
    
    • 管道的读端文件描述符和管道的写端文件描述符

    • 操作系统中的管道本质上是一块内存,unix中比较早的版本中就提出来了,主要是用来进程间的通信

    • 进程间的通信?

      1.读写同一个文件

      2.RPC通讯

      3.管道,在内存中开辟一块区域,在两个进程间搭起一个桥梁

  • 为什么要用fd0和fd1?

    • 在线程中断的时候,fd1会写入一个字节,fd0会读数据,同时会唤醒线程
  • implRegister方法

    • 1.获取一个相关文件描述符,不管是哪个channel
    • 2.把它的相关key缓存到fd2Key中
    • 3.pollWrapper.add(fd)
    • 4.keys.add(ski)
    • 但是这一堆注册都是jdk级别的注册,还没有操作系统级别的注册,实际是在调用select方法时才注册到操作系统
  • doSelect方法----做了三件事

    • 1.调用原生native方法(pollWrapper.poll—>epoll_wait)去获取已经就绪的文件描述符
    • 2.processDeregisterQueue----对取消注册的去做一个实际的取消
    • 3.updateSelectedKeys----哪一些key已经就绪,方便代码中去遍历和循环
  • processDeregisterQueue在select方法中为什么要调用两次?

    • poll是阻塞的,阻塞的时候有可能有的channel又关闭了

pollWrapper

  • 把底层对epoll的使用进行了保证

EPollArrayWrapper

  • 对epoll实际操作的一个封装

    • pollWrapper.poll,底层真正调用的都是linux底层对epoll的实现
  • EPOLLIN

    • epoll.c中进行事件注册时也用到了EPOLLIN
  • EPollArrayWrapper的构造方法

    • EPollArrayWrapper() throws IOException {
              
              
          // creates the epoll file descriptor
          epfd = epollCreate();
      
          // the epoll_event array passed to epoll_wait
          int allocationSize = NUM_EPOLLEVENTS * SIZE_EPOLLEVENT;
          pollArray = new AllocatedNativeObject(allocationSize, true);
          pollArrayAddress = pollArray.address();
      
          // eventHigh needed when using file descriptors > 64k
          if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
              eventsHigh = new HashMap<>();
      }
      
    • epfd = epollCreate();

      创建epoll的文件描述符

    • pollArray = new AllocatedNativeObject(allocationSize, true);
      pollArrayAddress = pollArray.address();
      

      1.维护一些与epoll读写相关的数组,比如说事件

      2.pollArray = new AllocatedNativeObject(allocationSize, true);

      直接在堆外创建一个对象,放在直接内存之上

    • // events for file descriptors with registration changes pending, indexed
      // by file descriptor and stored as bytes for efficiency reasons. For
      // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
      // least) then the update is stored in a map.
      private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
      private Map<Integer,Byte> eventsHigh;
      

      事件在保存时不要我们那么粗暴,做了调整,当文件描述符比较的小时候放到字节数组中,如果文件描述符比较大或者比较多的时候,就放在一个map中

  • 也提供了获取文件描述符、事件相关的各种方法

  • poll方法

    • updateRegistrations,更新相关注册信息

      这个方法里面会调用epollCtl,进行更新,如果是有的事件就更新,如果是没有的,就新增

    • epollWait—等待有通道就绪,目前有io事件发生了,这个方法有可能会有长时间的阻塞

private Map<Integer,SelectionKeyImpl> fdToKey;

  • 保存fd到key的映射关系

SelectionKey

  • 本质上是channel和selector联系的体现
  • 从SelectionKey获取channel
  • 从SelectionKey获取selector
  • 联系关系的维护
    • 检查是否可读
    • 检查是否可写
    • 检查是否可接受连接
    • 检查是否是连接的
  • SelectionKey都是int的,相关状态的变更都是通过位操作实现的

AbstractSelectionKey----SelectionKey的抽象类

  • cancel方法
    • 这个方法并不是马上取消,而是放到一个取消的集合,在适当的时候才执行

猜你喜欢

转载自blog.csdn.net/Markland_l/article/details/114537605