Java nio包 核心api解析:Selector、SelectionKey及Channel组件
This commit is contained in:
parent
8e6a53a557
commit
c14e9820d0
10
README.md
10
README.md
@ -127,32 +127,29 @@
|
||||
## Netty
|
||||
|
||||
### 网络 IO 技术基础
|
||||
|
||||
* [把被说烂的 BIO、NIO、AIO 再从头到尾扯一遍](docs/Netty/IOTechnologyBase/把被说烂的BIO、NIO、AIO再从头到尾扯一遍.md)
|
||||
* [IO模型](docs/Netty/IOTechnologyBase/IO模型.md)
|
||||
* [四种IO编程及对比](docs/Netty/IOTechnologyBase/四种IO编程及对比.md)
|
||||
|
||||
### Netty 粘拆包及解决方案
|
||||
### JDK1.8 NIO包 核心组件源码剖析
|
||||
* [Selector、SelectionKey及Channel组件](docs/Netty/IOTechnologyBase/Selector、SelectionKey及Channel组件.md)
|
||||
|
||||
### Netty 粘拆包及解决方案
|
||||
* [TCP粘拆包问题及Netty中的解决方案](docs/Netty/TCP粘拆包/TCP粘拆包问题及Netty中的解决方案.md)
|
||||
|
||||
### Netty 编解码
|
||||
|
||||
* [Java序列化缺点与主流编解码框架](docs/Netty/Netty编解码/Java序列化缺点与主流编解码框架.md)
|
||||
|
||||
### Netty 多协议开发
|
||||
|
||||
* [基于HTTP协议的Netty开发](docs/Netty/Netty多协议开发/基于HTTP协议的Netty开发.md)
|
||||
* [基于WebSocket协议的Netty开发](docs/Netty/Netty多协议开发/基于WebSocket协议的Netty开发.md)
|
||||
* [基于自定义协议的Netty开发](docs/Netty/Netty多协议开发/基于自定义协议的Netty开发.md)
|
||||
|
||||
### 基于 Netty 开发服务端及客户端
|
||||
|
||||
* [基于Netty的服务端开发](docs/Netty/基于Netty开发服务端及客户端/基于Netty的服务端开发.md)
|
||||
* [基于Netty的客户端开发](docs/Netty/基于Netty开发服务端及客户端/基于Netty的客户端开发.md)
|
||||
|
||||
### Netty 主要组件的源码分析
|
||||
|
||||
* [ByteBuf组件](docs/Netty/Netty主要组件源码分析/ByteBuf组件.md)
|
||||
* [Channel组件 和 Unsafe组件](docs/Netty/Netty主要组件源码分析/Channel和Unsafe组件.md)
|
||||
* [ChannelPipeline 和 ChannelHandler组件](docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md)
|
||||
@ -160,7 +157,6 @@
|
||||
* [Future 和 Promise组件](docs/Netty/Netty主要组件源码分析/Future和Promise组件.md)
|
||||
|
||||
### Netty 高级特性
|
||||
|
||||
* [Netty 架构设计](docs/Netty/AdvancedFeaturesOfNetty/Netty架构设计.md)
|
||||
* [Netty 高性能之道](docs/Netty/AdvancedFeaturesOfNetty/Netty高性能之道.md)
|
||||
|
||||
|
298
docs/Netty/IOTechnologyBase/Selector、SelectionKey及Channel组件.md
Normal file
298
docs/Netty/IOTechnologyBase/Selector、SelectionKey及Channel组件.md
Normal file
@ -0,0 +1,298 @@
|
||||
Selector、SelectionKey和Channel 这三个组件构成了Java nio包的核心,也是Reactor模型在代码层面的体现。Selector能让单线程同时处理多个客户端Channel,非常适用于高并发,传输数据量较小的场景。要使用Selector,首先要将对应的Channel及IO事件(读、写、连接)注册到Selector,注册后会产生一个SelectionKey对象,用于关联Selector和Channel,及后续的IO事件处理。这三者的关系如下图所示。
|
||||
|
||||

|
||||
|
||||
对nio编程不熟的同学可以搜索一些简单的demo跑一下,下面 我们直接进入源码,窥探一些nio的奥秘。
|
||||
### Selector
|
||||
其实,不管是 Selector 还是 SelectionKey 的源码,其具体实现类都是依赖于底层操作系统的,这里我们只看一下抽象类 Selector 的源码,日后有事件,再找一些具体的实现类深入分析一下。
|
||||
```java
|
||||
public abstract class Selector implements Closeable {
|
||||
|
||||
protected Selector() { }
|
||||
|
||||
/**
|
||||
* 获取一个 Selector对象,具体实现依赖于底层操作系统
|
||||
*/
|
||||
public static Selector open() throws IOException {
|
||||
return SelectorProvider.provider().openSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断该 Selector 是否已开启
|
||||
*/
|
||||
public abstract boolean isOpen();
|
||||
|
||||
/**
|
||||
* 当前所有向Selector注册的Channel 所对应的SelectionKey的集合
|
||||
*/
|
||||
public abstract Set<SelectionKey> keys();
|
||||
|
||||
/**
|
||||
* 相关事件已经被 Selector 捕获的 SelectionKey的集合
|
||||
*/
|
||||
public abstract Set<SelectionKey> selectedKeys();
|
||||
|
||||
/**
|
||||
* 阻塞到至少有一个通道在你注册的事件上就绪了
|
||||
*/
|
||||
public abstract int select() throws IOException;
|
||||
|
||||
/**
|
||||
* 和select()一样,除了最长会阻塞timeout毫秒
|
||||
*/
|
||||
public abstract int select(long timeout) throws IOException;
|
||||
|
||||
/**
|
||||
* 此方法执行非阻塞的选择操作,如果自从上一次选择操作后,
|
||||
* 没有通道变成可选择的,则此方法直接返回 0
|
||||
*/
|
||||
public abstract int selectNow() throws IOException;
|
||||
|
||||
/**
|
||||
* 用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效
|
||||
* 通道本身并不会关闭
|
||||
*/
|
||||
public abstract void close() throws IOException;
|
||||
}
|
||||
```
|
||||
|
||||
### SelectionKey
|
||||
表示 SelectableChannel 在 Selector 中的注册的标记 / 句柄。
|
||||
```java
|
||||
public abstract class SelectionKey {
|
||||
|
||||
protected SelectionKey() { }
|
||||
|
||||
|
||||
// -- Channel and selector operations --
|
||||
|
||||
/**
|
||||
* 获取该 SelectionKey 对应的Channel,Channel注册到Selector时会产生该 SelectionKey对象
|
||||
*/
|
||||
public abstract SelectableChannel channel();
|
||||
|
||||
/**
|
||||
* 获取该 SelectionKey 对应的 Selector
|
||||
*/
|
||||
public abstract Selector selector();
|
||||
|
||||
/**
|
||||
* 该 SelectionKey 是否是有效的
|
||||
*/
|
||||
public abstract boolean isValid();
|
||||
|
||||
// ------ Operation-set accessors ------
|
||||
|
||||
/**
|
||||
* 获取该 SelectionKey 的兴趣事件 (既 SelectionKey 的4个 事件静态常量)
|
||||
*/
|
||||
public abstract int interestOps();
|
||||
|
||||
/**
|
||||
* 设置该 SelectionKey 的兴趣事件
|
||||
*/
|
||||
public abstract SelectionKey interestOps(int ops);
|
||||
|
||||
/**
|
||||
* 获取该 SelectionKey 的已操作集
|
||||
*/
|
||||
public abstract int readyOps();
|
||||
|
||||
|
||||
// ------ Operation bits and bit-testing convenience methods ------
|
||||
|
||||
/**
|
||||
* channel中的数据是否已经可以读取
|
||||
*/
|
||||
public static final int OP_READ = 1 << 0;
|
||||
|
||||
/**
|
||||
* channel是否可以开始写入数据
|
||||
*/
|
||||
public static final int OP_WRITE = 1 << 2;
|
||||
|
||||
/**
|
||||
* channel是否已经建立连接
|
||||
*/
|
||||
public static final int OP_CONNECT = 1 << 3;
|
||||
|
||||
/**
|
||||
* ServerSocketChannel 是否可以与客户端建立连接
|
||||
*/
|
||||
public static final int OP_ACCEPT = 1 << 4;
|
||||
|
||||
/**
|
||||
* channel是否可读
|
||||
*/
|
||||
public final boolean isReadable() {
|
||||
return (readyOps() & OP_READ) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* channel是否可写
|
||||
*/
|
||||
public final boolean isWritable() {
|
||||
return (readyOps() & OP_WRITE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* channel是否建立连接
|
||||
*/
|
||||
public final boolean isConnectable() {
|
||||
return (readyOps() & OP_CONNECT) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServerSocketChannel是否可与客户端channel建立连接
|
||||
*/
|
||||
public final boolean isAcceptable() {
|
||||
return (readyOps() & OP_ACCEPT) != 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
### Channel组件
|
||||
平时编码用的比较多的就是 SocketChannel 和 ServerSocketChannel,而将 Channel 与 Selecor 关联到一起的核心API则定义在它们的公共父类SelectableChannel中,整个Channel组件的核心类图如下所示。
|
||||
|
||||

|
||||
|
||||
#### SelectableChannel
|
||||
```java
|
||||
public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel {
|
||||
|
||||
protected SelectableChannel() { }
|
||||
|
||||
/**
|
||||
* 当前channel是否注册到了某个selector上,新创建的channel都是未注册状态
|
||||
*/
|
||||
public abstract boolean isRegistered();
|
||||
|
||||
/**
|
||||
* 根据给定的 Selector,获取本channel注册上去的 SelectionKey
|
||||
*/
|
||||
public abstract SelectionKey keyFor(Selector sel);
|
||||
|
||||
/**
|
||||
* 将当前channel及关注的事件,注册到Selector上,返回一个 SelectionKey
|
||||
*/
|
||||
public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException {
|
||||
return register(sel, ops, null);
|
||||
}
|
||||
|
||||
public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;
|
||||
|
||||
/**
|
||||
* 设置该channel的阻塞模式,默认为 true阻塞
|
||||
*/
|
||||
public abstract SelectableChannel configureBlocking(boolean block) throws IOException;
|
||||
|
||||
/**
|
||||
* 是否为阻塞IO模式
|
||||
*/
|
||||
public abstract boolean isBlocking();
|
||||
}
|
||||
```
|
||||
|
||||
#### ServerSocketChannel
|
||||
相当于 BIO 中的 ServerSocket,主要用于服务端与客户端建立连接通信的channel。
|
||||
```java
|
||||
public abstract class ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel {
|
||||
|
||||
protected ServerSocketChannel(SelectorProvider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个 ServerSocketChannel实例,具体实现依赖底层操作系统
|
||||
*/
|
||||
public static ServerSocketChannel open() throws IOException {
|
||||
return SelectorProvider.provider().openServerSocketChannel();
|
||||
}
|
||||
|
||||
// -- ServerSocket-specific operations --
|
||||
|
||||
/**
|
||||
* 绑定ip地址及要监听的端口
|
||||
*/
|
||||
public final ServerSocketChannel bind(SocketAddress local) throws IOException {
|
||||
return bind(local, 0);
|
||||
}
|
||||
|
||||
public abstract ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException;
|
||||
|
||||
/**
|
||||
* 与一个客户端channel建立连接,返回该客户端的存根 SocketChannel
|
||||
*/
|
||||
public abstract SocketChannel accept() throws IOException;
|
||||
}
|
||||
```
|
||||
#### SocketChannel
|
||||
相当于 BIO 中的 Socket,主要用于通信双方的读写操作。
|
||||
```java
|
||||
public abstract class SocketChannel extends AbstractSelectableChannel
|
||||
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel {
|
||||
|
||||
protected SocketChannel(SelectorProvider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 SocketAddress 获取一个 SocketChannel,具体实现依赖底层操作系统
|
||||
*/
|
||||
public static SocketChannel open(SocketAddress remote) throws IOException {
|
||||
SocketChannel sc = open();
|
||||
try {
|
||||
sc.connect(remote);
|
||||
} catch (Throwable x) {
|
||||
try {
|
||||
sc.close();
|
||||
} catch (Throwable suppressed) {
|
||||
x.addSuppressed(suppressed);
|
||||
}
|
||||
throw x;
|
||||
}
|
||||
assert sc.isConnected();
|
||||
return sc;
|
||||
}
|
||||
|
||||
public static SocketChannel open() throws IOException {
|
||||
return SelectorProvider.provider().openSocketChannel();
|
||||
}
|
||||
|
||||
// -- Socket-specific operations --
|
||||
|
||||
/**
|
||||
* 绑定要连接的远程服务的ip及端口
|
||||
*/
|
||||
@Override
|
||||
public abstract SocketChannel bind(SocketAddress local) throws IOException;
|
||||
|
||||
/**
|
||||
* 该channel与服务端是否已连接
|
||||
*/
|
||||
public abstract boolean isConnected();
|
||||
|
||||
// -- ByteChannel operations --
|
||||
|
||||
/**
|
||||
* 将 channel 中的数据读到 ByteBuffer
|
||||
*/
|
||||
public abstract int read(ByteBuffer dst) throws IOException;
|
||||
|
||||
public final long read(ByteBuffer[] dsts) throws IOException {
|
||||
return read(dsts, 0, dsts.length);
|
||||
}
|
||||
|
||||
public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
|
||||
|
||||
/**
|
||||
* 将 ByteBuffer 中的数据写到 channel
|
||||
*/
|
||||
public abstract int write(ByteBuffer src) throws IOException;
|
||||
|
||||
public final long write(ByteBuffer[] srcs) throws IOException {
|
||||
return write(srcs, 0, srcs.length);
|
||||
}
|
||||
|
||||
public abstract long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
|
||||
}
|
||||
```
|
BIN
images/Netty/Channel组件.png
Normal file
BIN
images/Netty/Channel组件.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
images/Netty/Selector和SelectionKey和Channel关系图.png
Normal file
BIN
images/Netty/Selector和SelectionKey和Channel关系图.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
Loading…
x
Reference in New Issue
Block a user