概述
在文件IO的时候我们看到了,读取一个文件的时候,可以创建出一个 Channel
然后往 Channel里面读取 ByteBuffer
ByteBuffer 分为直接内存和堆内存,涉及到拷贝次数的问题.但是实际测试中,BIO和NIO的性能差别并不是很大,那么JDK为什么还要费尽周折搞NIO呢? 原因就是网络IO层面.在IO模型推导里面讲述了 非阻塞
IO的优势,这里就来细节的看JAVA是如何实现的。
Channel
还是先从Channel入手,网络的Channel分为服务端Channel ServerSocketChannel
和 客户端 SocketChannel
ServerSocketChannel
首先服务端的Channel实现了 NetworkChannel
, 可以绑定一个端口
public interface NetworkChannel
extends Channel
{
NetworkChannel bind(SocketAddress local) throws IOException;
}
再来看另一边的实现 SelectableChannel
public abstract class SelectableChannel
extends AbstractInterruptibleChannel
implements Channel
{
# 注册一个 Selector
public abstract SelectionKey register(Selector sel, int ops, Object att)
throws ClosedChannelException;
# 设置阻塞模式
public abstract SelectableChannel configureBlocking(boolean block)
throws IOException;
}
什么是 Selector先不谈,看过 IO模型的应该知道就是一个多路复用器,这里先略过,还有一个是设置这个 channel的阻塞模式。接下里看看 ServerSocketChannel
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel
{
# 创建 ServerSocketChannel的实现类
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
# 绑定端口
public abstract ServerSocketChannel bind(SocketAddress local, int backlog)
throws IOException;
# 接受客户端
public abstract SocketChannel accept() throws IOException;
}
服务端的 ServerSocketChannel 总结起来就是有一个
- 绑定端口
bind-> NetworkChannel
- 注册多路复用器和设置阻塞模型
register,configureBlocking -> SelectableChannel
- 接受客户端的连接
accept-> ServerSocketChannel
并不像文件IO的Channel通过ByteBuffer读写数据.
SocketChannel
接下来看看客户端的Channel
其他和服务端的一样,多出来了一些 ReadableByteChannel, WritableByteChannel 就知道肯定跟ByteBuffer读写相关了。
public interface ReadableByteChannel extends Channel {
public int read(ByteBuffer dst) throws IOException;
}
public interface WritableByteChannel extends Channel
{
public int write(ByteBuffer src) throws IOException;
}
看看 SocketChannel 源码
public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
{
# 创建一个socketchannel
public static SocketChannel open() throws IOException {
return SelectorProvider.provider().openSocketChannel();
}
# 连接远程服务器
public abstract boolean connect(SocketAddress remote) throws IOException;
# 读写
public abstract int write(ByteBuffer src) throws IOException;
public abstract int read(ByteBuffer dst) throws IOException;
}
客户端的SocketChannel总结起来就是
- 连接服务端
connect -> SocketChannel
- 读写
bytebuffer -> ReadableByteChannel,WritableByteChannel
Selector
看完了大体的客户端和服务单的 SocketChannel,我们来看看 服务端需要注册的 Selector是一个什么东西.
public abstract class Selector implements Closeable {
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
# 返回有IO事件的 selectionKey
public abstract Set<SelectionKey> selectedKeys();
# 阻塞的等待一组 SelectionKey,直到其中有一个或多个有IO事件
public abstract int select() throws IOException;
}
沒有继承什么类,就是一个等待返回一组有数据的对象,其中又涉及到其他的类 SelectionKey
. SelectionKey 的设计思想就是把 Channel和Selector绑定在一起
public abstract class SelectionKey {
public abstract SelectableChannel channel();
public abstract Selector selector();
public abstract int interestOps();
private volatile Object attachment = null;
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
}
register流程
register的本质就是创建一个 SelectionKey,然后 selector和Channel各自都保存一份. 回顾一下register是 SelectableChannel 的一个抽象方法.当一个 Channel调用register后
public final SelectionKey register(Selector sel, int ops,
Object att)
throws ClosedChannelException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (blocking)
throw new IllegalBlockingModeException();
# 判断有没有注册过
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
# 如果没有SelectionKey 调用 Selector的register,注册并且返回一个key
k = ((AbstractSelector)sel).register(this, ops, att);
# 自己在添加一个selectionKey
addKey(k);
}
}
return k;
}
}
我们来看看selector中的register的代码
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
if (!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
# 创建一个 selectionKey,绑定channel和自身
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
var4.attach(var3);
# 调用系统调用相关,根据操作系统不同而不同
synchronized(this.publicKeys) {
this.implRegister(var4);
}
var4.interestOps(var2);
return var4;
}
}