java nio过程_java NIO 运行原理介绍

开篇

回想研究生期间在H3C做项目的时候第一次接触epoll的异步事件,心血来潮看了下java的NIO的实现,希望同样感兴趣的人一起看看。Netty是java NIO的集大成者,一定要看看。

java NIO server demo

7b507069debb

socket server端工作标准流程

创建socket: 创建ServerSocketChannel,通过ServerSocketChannel.open()方法。

绑定socket:ServerSocketChannel绑定端口,通过serverSocketChannel.bind()方法。

前置准备: 创建selector对象,通过Selector.open()方法。

前置准备: 注册Channel到selector并绑定事件,通过serverSocketChannel.register()。

监听端口号: 通过listen()方法开始进入监听。

处理事件: while循环中等待select操作返回区分连接还是数据进行不同处理。

public class NIOServer {

private Selector selector;

public void initServer(int port) throws IOException {

// 获得一个ServerSocketChannel通道

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

// 设置通道为非阻塞

serverSocketChannel.configureBlocking(false);

// 将该通道对应的ServerSocket绑定到port端口

serverSocketChannel.bind(new InetSocketAddress(port));

// 获得一个通道管理器

this.selector = Selector.open();

// 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,

// 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

}

public void listen() throws IOException {

System.out.println("服务端启动成功!");

// 轮询访问selector

while (true) {

// 当注册的事件到达时,方法返回;否则,该方法会一直阻塞

selector.select();

// 获得selector中选中的项的迭代器,选中的项为注册的事件

Iterator ite = this.selector.selectedKeys().iterator();

while (ite.hasNext()) {

SelectionKey key = (SelectionKey) ite.next();

// 删除已选的key,以防重复处理

ite.remove();

if (key.isAcceptable()) {// 客户端请求连接事件

ServerSocketChannel server = (ServerSocketChannel) key.channel();

// 获得和客户端连接的通道

SocketChannel channel = server.accept();

// 设置成非阻塞

channel.configureBlocking(false);

// 在这里可以给客户端发送信息哦

channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息")

.getBytes("utf-8")));

// 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。

channel.register(this.selector, SelectionKey.OP_READ);

} else if (key.isReadable()) {// 获得了可读的事件

read(key);

}

}

}

}

public void read(SelectionKey key) throws IOException {

// 服务器可读取消息:得到事件发生的Socket通道

SocketChannel channel = (SocketChannel) key.channel();

// 创建读取的缓冲区

ByteBuffer buffer = ByteBuffer.allocate(512);

channel.read(buffer);

byte[] data = buffer.array();

String msg = new String(data).trim();

System.out.println("服务端收到信息:" + msg);

ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));

channel.write(outBuffer);// 将消息回送给客户端

}

public static void main(String[] args) throws IOException {

NIOServer server = new NIOServer();

server.initServer(8000);

server.listen();

}

}

ServerSocketChannel和Selector初始化过程

在java NIO Server的标准过程中,有两个核心的操作需要深入分析一下,分别是ServerSocketChannel.open() 和 Selector.open()两个过程,这里针对这两个对象的初始化流程进行下细致的分解。

// ServerSocketChannel的初始化过程

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

// Selector的初始化过程

selector = Selector.open();

通用逻辑抽取

ServerSocketChannel.open()=SelectorProvider.provider().openServerSocketChannel()

Selector.open()=SelectorProvider.provider().openSelector()

两者有共同点在于都调用了SelectorProvider.provider()方法,所以先把相同部分进行分析。

public abstract class ServerSocketChannel extends AbstractSelectableChannel

implements NetworkChannel

{

protected ServerSocketChannel(SelectorProvider provider) {

super(provider);

}

public static ServerSocketChannel open() throws IOException {

return SelectorProvider.provider().openServerSocketChannel();

}

}

public abstract class Selector implements Closeable {

protected Selector() { }

public static Selector open() throws IOException {

return SelectorProvider.provider().openSelector();

}

}

SelectorProvider对象创建

SelectorProvider.provider()方法会在内部创建唯一的SelectorProvider对象,通过锁来保证创建唯一对象。

SelectorProvider对象通过DefaultSelectorProvider.create()方法进行创建。

DefaultSelectorProvider.create()方法内部根据实际系统创建不同的对象,以linux环境中EPollSelectorProvider对象为例继续分析。

public abstract class SelectorProvider {

private static final Object lock = new Object();

private static SelectorProvider provider = null;

public static SelectorProvider provider() {

synchronized (lock) {

if (provider != null)

return provider;

return AccessController.doPrivileged(

new PrivilegedAction() {

public SelectorProvider run() {

if (loadProviderFromProperty())

return provider;

if (loadProviderAsService())

return provider;

provider = sun.nio.ch.DefaultSelectorProvider.create();

return provider;

}

});

}

}

}

public class DefaultSelectorProvider {

public static SelectorProvider create() {

String osname = AccessController.doPrivileged(

new GetPropertyAction("os.name"));

if ("SunOS".equals(osname)) {

return new sun.nio.ch.DevPollSelectorProvider();

}

// use EPollSelectorProvider for Linux kernels >= 2.6

if ("Linux".equals(osname)) {

String osversion = AccessController.doPrivileged(

new GetPropertyAction("os.version"));

String[] vers = osversion.split("\\.", 0);

if (vers.length >= 2) {

try {

int major = Integer.parseInt(vers[0]);

int minor = Integer.parseInt(vers[1]);

if (major > 2 || (major == 2 && minor >= 6)) {

return new sun.nio.ch.EPollSelectorProvider();

}

} catch (NumberFormatException x) {

// format not recognized

}

}

}

return new sun.nio.ch.PollSelectorProvider();

}

}

EPollSelectorProvider的操作过程

SelectorProvider.provider().openServerSocketChannel()调用EPollSelectorProvider的openServerSocketChannel()方法返回EPollSelectorImpl对象。

SelectorProvider.provider().openSelector()调用EPollSelectorProvider的openSelector()方法返回ServerSocketChannelImpl对象。

继续分析ServerSocketChannelImpl对象和EPollSelectorImpl对象。

public class EPollSelectorProvider extends SelectorProviderImpl

{

public AbstractSelector openSelector() throws IOException {

return new EPollSelectorImpl(this);

}

public Channel inheritedChannel() throws IOException {

return InheritedChannel.getChannel();

}

}

public abstract class SelectorProviderImpl extends SelectorProvider

{

public DatagramChannel openDatagramChannel() throws IOException {

return new DatagramChannelImpl(this);

}

public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {

return new DatagramChannelImpl(this, family);

}

public Pipe openPipe() throws IOException {

return new PipeImpl(this);

}

public abstract AbstractSelector openSelector() throws IOException;

public ServerSocketChannel openServerSocketChannel() throws IOException {

return new ServerSocketChannelImpl(this);

}

public SocketChannel openSocketChannel() throws IOException {

return new SocketChannelImpl(this);

}

}

EPollSelectorImpl对象

EPollSelectorImpl构造函数创建内部通信的socket对IOUtil.makePipe(false)。

EPollSelectorImpl的doSelect方法负责返回事件到来的fds。

EPollSelectorImpl的fdToKey的map保存fd和SelectionKey的映射。

class EPollSelectorImpl extends SelectorImpl

{

// File descriptors used for interrupt

protected int fd0;

protected int fd1;

// The poll object

EPollArrayWrapper pollWrapper;

// Maps from file descriptors to keys

private Map fdToKey;

// True if this Selector has been closed

private volatile boolean closed = false;

// Lock for interrupt triggering and clearing

private Object interruptLock = new Object();

private boolean interruptTriggered = false;

EPollSelectorImpl(SelectorProvider sp) {

super(sp);

long pipeFds = IOUtil.makePipe(false);

fd0 = (int) (pipeFds >>> 32);

fd1 = (int) pipeFds;

pollWrapper = new EPollArrayWrapper();

pollWrapper.initInterrupt(fd0, fd1);

fdToKey = new HashMap();

}

protected int doSelect(long timeout) throws IOException

{

if (closed)

throw new ClosedSelectorException();

processDeregisterQueue();

try {

begin();

// 等待事件到来,收集事件到来的socket的fd并用来处理

pollWrapper.poll(timeout);

} finally {

end();

}

processDeregisterQueue();

// 更新需要写入的keys

int numKeysUpdated = updateSelectedKeys();

if (pollWrapper.interrupted()) {

// Clear the wakeup pipe

pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0);

synchronized (interruptLock) {

pollWrapper.clearInterrupted();

IOUtil.drain(fd0);

interruptTriggered = false;

}

}

return numKeysUpdated;

}

private int updateSelectedKeys() {

int entries = pollWrapper.updated;

int numKeysUpdated = 0;

for (int i=0; i

int nextFD = pollWrapper.getDescriptor(i);

SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));

// ski is null in the case of an interrupt

if (ski != null) {

int rOps = pollWrapper.getEventOps(i);

if (selectedKeys.contains(ski)) {

if (ski.channel.translateAndSetReadyOps(rOps, ski)) {

numKeysUpdated++;

}

} else {

ski.channel.translateAndSetReadyOps(rOps, ski);

if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {

// selectedKeys保存ski也就是事件到的socket连接

// ski的对象数据结构需要好好研究一下

selectedKeys.add(ski);

numKeysUpdated++;

}

}

}

}

return numKeysUpdated;

}

}

ServerSocketChannelImpl对象

ServerSocketChannelImpl extends ServerSocketChannel

ServerSocketChannel extends AbstractSelectableChannel

ServerSocketChannelImpl对象提供bind()&accept()方法

ServerSocketChannelImpl的accept方法内部创建新连接的SocketChannelImpl对象返回

class ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl

{

private final Object stateLock = new Object();

private SocketAddress localAddress;

ServerSocket socket;

ServerSocketChannelImpl(SelectorProvider sp) throws IOException {

super(sp);

this.fd = Net.serverSocket(true);

this.fdVal = IOUtil.fdVal(fd);

this.state = ST_INUSE;

}

ServerSocketChannelImpl(SelectorProvider sp,

FileDescriptor fd,

boolean bound) throws IOException

{

super(sp);

this.fd = fd;

this.fdVal = IOUtil.fdVal(fd);

this.state = ST_INUSE;

if (bound)

localAddress = Net.localAddress(fd);

}

@Override

public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {

// 省略相关代码

}

public SocketChannel accept() throws IOException {

// 省略相关代码

}

}

public abstract class AbstractSelectableChannel extends SelectableChannel

{

protected AbstractSelectableChannel(SelectorProvider provider) {

this.provider = provider;

}

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();

k = ((AbstractSelector)sel).register(this, ops, att);

addKey(k);

}

}

return k;

}

}

}

select过程

select执行过程

执行selector.select()操作时实际是调用了子类实现的doSelect()方法。

进一步跟进子类的doSelect()方法。

abstract class SelectorImpl extends AbstractSelector

{

// 保存事件到来的keys

protected Set selectedKeys;

protected HashSet keys;

private Set publicKeys; // Immutable

private Set publicSelectedKeys; // Removal allowed, but not addition

protected abstract int doSelect(long timeout) throws IOException;

private int lockAndDoSelect(long timeout) throws IOException {

synchronized (this) {

if (!isOpen())

throw new ClosedSelectorException();

synchronized (publicKeys) {

synchronized (publicSelectedKeys) {

return doSelect(timeout);

}

}

}

}

public int select(long timeout) throws IOException

{

if (timeout < 0)

throw new IllegalArgumentException("Negative timeout");

return lockAndDoSelect((timeout == 0) ? -1 : timeout);

}

public int select() throws IOException {

return select(0);

}

public Set selectedKeys() {

if (!isOpen() && !Util.atBugLevel("1.4"))

throw new ClosedSelectorException();

return publicSelectedKeys;

}

}

pollWrapper.poll(timeout)以超时等待的形式等待epoll的消息通知。

通过updateSelectedKeys方法收集有事件到达的fds保存到selectedKeys。

class EPollSelectorImpl extends SelectorImpl

{

protected int doSelect(long timeout) throws IOException

{

if (closed)

throw new ClosedSelectorException();

processDeregisterQueue();

try {

begin();

// 等待事件到来,收集事件到来的socket的fd并用来处理

pollWrapper.poll(timeout);

} finally {

end();

}

processDeregisterQueue();

// 更新需要写入的keys

int numKeysUpdated = updateSelectedKeys();

if (pollWrapper.interrupted()) {

// Clear the wakeup pipe

pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0);

synchronized (interruptLock) {

pollWrapper.clearInterrupted();

IOUtil.drain(fd0);

interruptTriggered = false;

}

}

return numKeysUpdated;

}

private int updateSelectedKeys() {

int entries = pollWrapper.updated;

int numKeysUpdated = 0;

for (int i=0; i

int nextFD = pollWrapper.getDescriptor(i);

SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));

// ski is null in the case of an interrupt

if (ski != null) {

int rOps = pollWrapper.getEventOps(i);

if (selectedKeys.contains(ski)) {

if (ski.channel.translateAndSetReadyOps(rOps, ski)) {

numKeysUpdated++;

}

} else {

ski.channel.translateAndSetReadyOps(rOps, ski);

if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {

// selectedKeys保存ski也就是事件到的socket连接

// ski的对象数据结构需要好好研究一下

selectedKeys.add(ski);

numKeysUpdated++;

}

}

}

}

return numKeysUpdated;

}

}

accept过程

accept的过程很简单就是accept新socket并创建SocketChannelImpl返回即可。

SocketChannelImpl对象后面需要注册到Selector当中所以需要进一步分析。

public SocketChannel accept() throws IOException {

// 省略相关代码

try {

// 省略相关代码

// 新accept的socket放在newfd当中

n = accept0(this.fd, newfd, isaa);

}

}

IOUtil.configureBlocking(newfd, true);

InetSocketAddress isa = isaa[0];

// 通过SocketChannelImpl包装newfd对象

sc = new SocketChannelImpl(provider(), newfd, isa);

// 省略相关代码

return sc;

}

}

SocketChannelImpl对象

SocketChannelImpl可以理解为普通Socket的封装,包括read/write等方法

SocketChannelImpl extends SocketChannel extends AbstractSelectableChannel

AbstractSelectableChannel提供register到selector对象的方法

class SocketChannelImpl extends SocketChannel implements SelChImpl

{

SocketChannelImpl(SelectorProvider sp) throws IOException {

super(sp);

this.fd = Net.socket(true);

this.fdVal = IOUtil.fdVal(fd);

this.state = ST_UNCONNECTED;

}

SocketChannelImpl(SelectorProvider sp,

FileDescriptor fd,

boolean bound)

throws IOException

{

super(sp);

this.fd = fd;

this.fdVal = IOUtil.fdVal(fd);

this.state = ST_UNCONNECTED;

if (bound)

this.localAddress = Net.localAddress(fd);

}

SocketChannelImpl(SelectorProvider sp,

FileDescriptor fd, InetSocketAddress remote)

throws IOException

{

super(sp);

this.fd = fd;

this.fdVal = IOUtil.fdVal(fd);

this.state = ST_CONNECTED;

this.localAddress = Net.localAddress(fd);

this.remoteAddress = remote;

}

public long read(ByteBuffer[] dsts, int offset, int length)

throws IOException

{

// 读数据的逻辑

}

public int write(ByteBuffer buf) throws IOException {

// 写数据的逻辑

}

}

register过程

register过程并没有调用epollCtl方法添加fd到selector当中

register过程真正是保存fd到待绑定的列表当中

在SelectorImpl中执行pollWrapper.poll(timeout)方法先把fd列表执行epollCtl添加selector当中,在通过epollWait获取事件到来

public abstract class AbstractSelectableChannel extends SelectableChannel

{

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();

k = ((AbstractSelector)sel).register(this, ops, att);

addKey(k);

}

}

return k;

}

}

}

abstract class SelectorImpl extends AbstractSelector

{

protected final SelectionKey register(AbstractSelectableChannel ch,

int ops,

Object attachment)

{

if (!(ch instanceof SelChImpl))

throw new IllegalSelectorException();

SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);

k.attach(attachment);

synchronized (publicKeys) {

implRegister(k);

}

k.interestOps(ops);

return k;

}

}

abstract class AbstractPollSelectorImpl extends SelectorImpl

{

protected void implRegister(SelectionKeyImpl ski) {

synchronized (closeLock) {

if (closed)

throw new ClosedSelectorException();

// Check to see if the array is large enough

if (channelArray.length == totalChannels) {

// Make a larger array

int newSize = pollWrapper.totalChannels * 2;

SelectionKeyImpl temp[] = new SelectionKeyImpl[newSize];

// Copy over

for (int i=channelOffset; i

temp[i] = channelArray[i];

channelArray = temp;

// Grow the NativeObject poll array

pollWrapper.grow(newSize);

}

channelArray[totalChannels] = ski;

ski.setIndex(totalChannels);

// 核心的将channel添加到pollWrapper当中

pollWrapper.addEntry(ski.channel);

totalChannels++;

keys.add(ski);

}

}

}

class EPollArrayWrapper {

int poll(long timeout) throws IOException {

updateRegistrations();

updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd);

for (int i=0; i

if (getDescriptor(i) == incomingInterruptFD) {

interruptedIndex = i;

interrupted = true;

break;

}

}

return updated;

}

void updateRegistrations() {

synchronized (updateList) {

Updator u = null;

while ((u = updateList.poll()) != null) {

SelChImpl ch = u.channel;

if (!ch.isOpen())

continue;

// if the events are 0 then file descriptor is put into "idle

// set" to prevent it being polled

if (u.events == 0) {

boolean added = idleSet.add(u.channel);

// if added to idle set then remove from epoll if registered

if (added && (u.opcode == EPOLL_CTL_MOD))

epollCtl(epfd, EPOLL_CTL_DEL, ch.getFDVal(), 0);

} else {

// events are specified. If file descriptor was in idle set

// it must be re-registered (by converting opcode to ADD)

boolean idle = false;

if (!idleSet.isEmpty())

idle = idleSet.remove(u.channel);

int opcode = (idle) ? EPOLL_CTL_ADD : u.opcode;

epollCtl(epfd, opcode, ch.getFDVal(), u.events);

}

}

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值