NIO SelectionKey事件理解

在Java NIO编程中,我们可以在通道上注册OP_ACCEPTOP_CONNECTOP_READOP_WRITE,下面我们分别看下各种事件在源码中的注释说明:

  • OP_ACCEPT

Operation-set bit for socket-accept operations. Suppose that a selection key's interest set contains OP_ACCEPT at the start of a selection operatio. If the selector detects that the corresponding server-socket channel is ready to accept another connection, or has an error pending, then it will add OP_ACCEPT to the key's ready set and add the key to its selected-key set.

表明一个SelectionKey包含Accept事件,当selector检测到关联的server-socket channel准备好接受一个连接或者发生错误时,会将该事件加入到该key的'ready set'中,并将该key加入到selected-key set中。

  • OP_CONNECT

Operation-set bit for socket-connect operations. Suppose that a selection key's interest set contains OP_CONNECT at the start of a selection operation. If the selector detects that the corresponding socket channel is ready to complete its connection sequence, or has an error pending, then it will add OP_CONNECT to he key's ready set and add the key to its selected-key set.

当一个socket channel已经准备好完成连接操作或者发生错误时会将该事件加入到该key的'ready set'中,并将该key加入到selected-key set中。

  • OP_READ

Operation-set bit for read operations. Suppose that a selection key's interest set contains OP_READ at the start of a selection operation. If the selector detects that the corresponding channel is ready for reading, has reached end-of-stream, has been remotely shut down for further reading, or has an error pending, then it will add OP_READ to the key's ready-operation set and add the key to its selected-key set.

当一个channel准备好可以被读取(比如socket已经读到了新的字节,其读取缓冲区有尚未被应用读取的数据,可通知应用程序从读取缓冲区进行读取)、已经到达数据流的结尾、远程另一端已经关闭或者发生错误时,会将该事件加入到该key的'ready set'中,并将该key加入到selected-key set中。

  • OP_WRITE

Operation-set bit for write operations. Suppose that a selection key's interest set contains OP_WRITE at the start of a selection operation. If the selector detects that the corresponding channel is ready for writing, has been remotely shut down for further writing, or has an error pending, then it will add OP_WRITE to the key's ready set and add the key to its selected-key set.

当一个channel 准备好进行写入操作(比如原先socket写入缓冲已满,现在已经发送了部分数据,写入缓冲腾出了一些空间,会通知应用程序进行写入)、远程另一端已经关闭或者发生错误时,会将该事件加入到该key的'ready set'中,并将该key加入到selected-key set中。

通过上面的注释,我们可以知道注册事件只是告诉selector在检测到各种事件准备好可以执行时告诉应用,应用可以执行相应的事件,但是注册感兴趣的事件并不是必须的,没有注册事件依然可以进行读取、写入操作,比如读取操作,如果不注册OP_READ事件,应用程序依然可以随时尝试从channel中读取数据,但是并不能保证读取成功,因为可能socket读取缓冲区中可能并不存在数据。但是如果注册了OP_READ事件,在selector检测到该事件准备好了再去读取,则可以最大程度上的保证可以读到数据。写入也是一样的道理,不注册OP_WRITE应用程序也可以随时进行写入,但是可能因为缓冲区满而写入失败,如果注册了OP_WRITE事件,在selector检测到该事件时再进行写入,可以最大程度上保证写入成功。所以说注册相关事件不是必须的,注册了事件可以由selector检测到事件准备好可以执行了再执行相关的事件,避免了许多无效的尝试。

下面我们可以看下Selector.select方法的源码注释:

Selects a set of keys whose corresponding channels are ready for I/O operations. This method performs a blocking selection operation. It returns only after at least one channel is selected, this selector's wakeup method is invoked, or the current thread is interrupted, whichever comes first.

当注册在该Selector上的某个Channel的至少一个事件准备好之后才会返回。因为读取事件是一个被动的事件,应用程序一般会等待读取数据,所在我们一般会注册读取事件,在Channel准备好读取之后告诉应用程序进行读取。而写事件属于一种主动的事件,我们一般不会注册写事件,有要写的数据则直接写数据至Channel即可,写入失败则表示Channel暂时没有准备好被写入,此时应用可以向Channel注册写事件,让Channel在准备好接收写事件时告诉应用,但是一旦应用写完所有的数据,则一般会取消注册的写事件,因为如果数据写完了,但是没有取消注册的写事件,Selector.select方法可能会经常因为有写事件准备完毕而返回,但是应用却无数据需要写。

关于写事件的注册与取消注册,大家可以看下Netty向网络中写数据的实现,正常情况下不会注册写事件,只是在发生写方法返回0,或者写半包的情况下,也即Channel没有准备好写入或者写缓冲已满,但是应用还有数据需要写的情况下才注册写事件,让Channel在准备好写入时及时通知应用,然后应用可以继续写,写完之后会及时取消对写事件的注册。后面会有专文介绍Netty Write原理。

你可能感兴趣的:(NIO SelectionKey事件理解)