以NIOServer示例代码分析java NIO的底层原理

一、NIOServer示例代码

package io.netty.example.javanio_test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOServer {
    private Selector selector;

    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        //初始化服务器,绑定8888端口
        server.initServer(8888);
        //监听客户端请求
        server.listen();
    }

    public void initServer(int port) throws IOException{
        // 打开ServerSocket通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //绑定方式是将ServerSocketChannel对应的FileDescriptor与本地的地址和端口进行绑定
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        // 获取一个选择器
        this.selector = Selector.open();
        // 将通道管理器与该通道进行绑定,并为该通道注册SelectionKey.OP_ACCEPT事件
        // 注册事件后,当该事件触发时会使selector.select()返回,
        // 否则selector.select()一直阻塞
        serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
    }

    public void listen() throws IOException{
        System.out.println("启动服务器!");
        while (true) {
            // select()方法一直阻塞直到有注册的通道准备好了才会返回
            selector.select();
            Iterator<?> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = (SelectionKey) iterator.next();
                // 删除已选的key,防止重复处理
                iterator.remove();
                handler(key);
            }
        }
    }

    public void handler(SelectionKey key)throws IOException{
        if (key.isAcceptable()) {//接收客户端请求
            handlerAccept(key);
        }else if (key.isReadable()){//处理读
            handlerRead(key);
        }else if (key.isWritable()){//处理写
            System.out.println("can write!");
        }else if (key.isConnectable()){//连接
            System.out.println("is connectable");
        }
    }


    public void handlerAccept(SelectionKey key) throws IOException{
        // 从SelectionKey中获取ServerSocketChannel
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        // 获取SocketChannel
        SocketChannel socketChannel = server.accept();
        // 设置成非阻塞
        socketChannel.configureBlocking(false);
        System.out.println("与客户端建立连接");
        // 为socketChannel通道建立 OP_READ 读操作,使客户端发送的内容可以被读到
        socketChannel.register(selector, SelectionKey.OP_READ);
        // 往客户端发送发送信息
        socketChannel.write(ByteBuffer.wrap("connected\n".getBytes()));
    }

    public void handlerRead(SelectionKey key)throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        // 创建读取缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        // 从通道读取可读取的字节数
        try {
            int readcount = socketChannel.read(byteBuffer);
            if (readcount > 0) {
                byte[] data = byteBuffer.array();
                String msg = new String(data);
                System.out.println("服务端收到的信息为:\n" + msg);
                ByteBuffer outBuffer = ByteBuffer.wrap("收到\n".getBytes());
                socketChannel.write(outBuffer);
            } else {
                System.out.println("客户端异常退出");

            }
        } catch (IOException e) {
            System.out.println("异常信息:\n" + e.getMessage());
            key.cancel();
        }
    }
}

 

二、代码分析

1、initServer方法分析

(1)打开ServerSocketChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        1)调用ServerSocketChannel.open()方法

public static ServerSocketChannel open() throws IOException {
    return SelectorProvider.provider().openServerSocketChannel();
}
  • 创建SelectorPriverder,调用SelectorProvider的provider方法。本例是在windows系统上运行的
public static SelectorProvider provider() {
    synchronized (lock) {
        if (provider != null)
            return provider;
        //安全访问
        return AccessController.doPrivileged(
            new PrivilegedAction<SelectorProvider>() {
                public SelectorProvider run() {
                        //按property方式加载provider,不做具体分析
                        if (loadProviderFromProperty())
                            return provider;
                        //按service方式加载provider,不做具体分析
                        if (loadProviderAsService())
                            return provider;
                        //默认的provider创建方式,本例采用的是如下的创建方式
                        provider = sun.nio.ch.DefaultSelectorProvider.create();
                        return provider;
                    }
                });
    }
}
  •    调用DefaultSelectorProvider的create()方法,使用new方式创建WindowsSelectorProvider
public static SelectorProvider create() {
    return new WindowsSelectorProvider();
}
  • 回过头来看openServerSocketChannel方法,实际上调用到了SelectorProviderImpl的对应的方法。使用new方式创建ServerSocketChannelImpl
public ServerSocketChannel openServerSocketChannel() throws IOException {
    return new ServerSocketChannelImpl(this);
}
  • 接下来看ServerSocketChannelImpl的构造方法
ServerSocketChannelImpl(SelectorProvider var1) throws IOException {
    //调用父类构造器,将SelectorProvider赋值给父类的属性
    super(var1);
    //新建FileDescriptor,此时即将fd与ServerSocketChannel进行绑定
    this.fd = Net.serverSocket(true);
    //给fdVal赋值
    this.fdVal = IOUtil.fdVal(this.fd);
    //初始化通道状态为0
    this.state = 0;
}

2)配置ServerSocketChannel为非阻塞模式

serverSocketChannel.configureBlocking(false);

3)ServerSocketChannel的绑定

//绑定本地8888端口
serverSocketChannel.socket().bind(new InetSocketAddress(port));
  • socket的创建,调用ServerSocketChannelImpl的socket()方法
public ServerSocket socket() {
    synchronized(this.stateLock) {
        if (this.socket == null) {
            //此处ServerSocketAdaptor继承了ServerSocket,并将ServerSocketChannelImpl赋值给ServerSocketAdaptor属性
            this.socket = ServerSocketAdaptor.create(this);
        }

        return this.socket;
    }
}
  • 再看ServerSocket的bind方法
public void bind(SocketAddress endpoint) throws IOException {
    bind(endpoint, 50);
}
  • 继续分析重载的bind方法
public void bind(SocketAddress endpoint, int backlog) throws IOException {
    //判断Socket是否关闭
    if (isClosed())
        throw new SocketException("Socket is closed");
    //判断是否已经绑定
    if (!oldImpl && isBound())
        throw new SocketException("Already bound");
    //判断绑定的地址是否为空,如果为空则新建地址,并绑定0端口
    if (endpoint == null)
        endpoint = new InetSocketAddress(0);
    //判断是否为InetSocketAddress类型实例
    if (!(endpoint instanceof InetSocketAddress))
        throw new IllegalArgumentException("Unsupported address type");
    InetSocketAddress epoint = (InetSocketAddress) endpoint;
    //判断地址是否未解析
    if (epoint.isUnresolved())
        throw new SocketException("Unresolved address");
    if (backlog < 1)
      backlog = 50;
    try {
        SecurityManager security = System.getSecurityManager();
        if (security != null)
            security.checkListen(epoint.getPort());
        //getImpl()方法所创建的实例未SocksSocketImpl,SocksSocketImpl继承了PlainSocketImpl,PlainSocketImpl又继承了                AbstractPlainSocketImpl
        getImpl().bind(epoint.getAddress(), epoint.getPort());
        //监听
        getImpl().listen(backlog);
        bound = true;
    } catch(SecurityException e) {
        bound = false;
        throw e;
    } catch(IOException e) {
        bound = false;
        throw e;
    }
}
  • PlainSocketImpl的构造方法,本例中impl创建为DualStackPlainSocketImpl
PlainSocketImpl() {
    if (useDualStackImpl) {
        impl = new DualStackPlainSocketImpl(exclusiveBind);
    } else {
        impl = new TwoStacksPlainSocketImpl(exclusiveBind);
    }
}
  • 回头看getImpl().bind(...)方法,实际上调用到AbstractSocksSocketImpl的bind方法
protected synchronized void bind(InetAddress address, int lport)
    throws IOException
{
   synchronized (fdLock) {
        if (!closePending && (socket == null || !socket.isBound())) {
            NetHooks.beforeTcpBind(fd, address, lport);
        }
    }
    socketBind(address, lport);
    if (socket != null)
        socket.setBound();
    if (serverSocket != null)
        serverSocket.setBound();
}
  • socketBind(address,lport)方法分析,实际调用到DualStackPlainSocketImpl的socketBind(...)方法
void socketBind(InetAddress address, int port) throws IOException {
    //校验并获取到本地的FileDescriptor
    int nativefd = checkAndReturnNativeFD();

    if (address == null)
        throw new NullPointerException("inet address argument is null.");
    //调用native的bind0方法将FileDescriptor的fd与指定的地址和端口绑定
    bind0(nativefd, address, port, exclusiveBind);
    if (port == 0) {
        localport = localPort0(nativefd);
    } else {
        localport = port;
    }

    this.address = address;
}
  • bind0方法
static native void bind0(int fd, InetAddress localAddress, int localport,
                         boolean exclBind)
    throws IOException;

4)打开选择器

// 获取一个选择器
this.selector = Selector.open();
  • 调用Selector的open方法
public static Selector open() throws IOException {
    return SelectorProvider.provider().openSelector();
}
  • 上文讲过,此例在windows系统,已经创建了SelectorProvider为WindowsSelectorProvider,调用WindowsSelectorProvider的openSelector方法,使用new创建了WindowsSelectorImpl实例。对于WindowsSelectorImpl的构造方法,可以自行研究,这里不再赘述。
public AbstractSelector openSelector() throws IOException {
    return new WindowsSelectorImpl(this);
}

5)进行ServerSocketChannel的注册

serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
  • 调用到SelectableChannel的注册方法
public final SelectionKey register(Selector sel, int ops)
    throws ClosedChannelException
{
    return register(sel, ops, null);
}
  • 继续调用到AbstractSelectableChannel的register方法
public final SelectionKey register(Selector sel, int ops,
                                   Object att)
    throws ClosedChannelException
{
    synchronized (regLock) {
        //判断通道是否打开
        if (!isOpen())
            throw new ClosedChannelException();
        //判断通道感兴趣的事件是否为0
        if ((ops & ~validOps()) != 0)
            throw new IllegalArgumentException();
        //判断是否阻塞
        if (blocking)
            throw new IllegalBlockingModeException();
        //在给定的Selector中寻找SelectionKey
        SelectionKey k = findKey(sel);
        //key不为空,则将感兴趣的事件设置为Accept
        if (k != null) {
            k.interestOps(ops);
            k.attach(att);
        }
        if (k == null) {
            // New registration
            synchronized (keyLock) {
                if (!isOpen())
                    throw new ClosedChannelException();
                //key为空,则新建key并注册到selector上
                k = ((AbstractSelector)sel).register(this, ops, att);
                addKey(k);
            }
        }
        return k;
    }
}
 
  • findKey(...)方法的逻辑
private SelectionKey findKey(Selector sel) {
    synchronized (keyLock) {
        //keys为通道中维护的SelectionKey数组
        if (keys == null)
            return null;
        //如果keys数组中找个该key所对应的selector为所给定的selector,则返回该key
        for (int i = 0; i < keys.length; i++)
            if ((keys[i] != null) && (keys[i].selector() == sel))
                return keys[i];
        return null;
    }
}
  • SelectorImpl对应的register新的SelectionKey的逻辑
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
    if (!(var1 instanceof SelChImpl)) {
        throw new IllegalSelectorException();
    } else {
        //新建一个SelectionKey
        SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
        var4.attach(var3);
        synchronized(this.publicKeys) {
            //进行注册
            this.implRegister(var4);
        }
        //设置key感兴趣的事件
        var4.interestOps(var2);
        return var4;
    }
}
  • 调用WindowsSelectorImpl的implRegister方法
protected void implRegister(SelectionKeyImpl var1) {
    synchronized(this.closeLock) {
        if (this.pollWrapper == null) {
            throw new ClosedSelectorException();
        } else {
            this.growIfNeeded();
            //channelArray数组中新增一个Channel
            this.channelArray[this.totalChannels] = var1;
            //设置Selectionkey的index
            var1.setIndex(this.totalChannels);
            //将SelectionKey放入fdMap中
            this.fdMap.put(var1);
            //将SelectionKey放入key set 中
            this.keys.add(var1);
            //将SelectionKey放入pollWrapper中
            this.pollWrapper.addEntry(this.totalChannels, var1);
            //selector的channel总数加1
            ++this.totalChannels;
        }
    }
}

2、listen方法分析

(1)selector.select()方法分析

  • 调用到SelectorImpl的select方法
public int select() throws IOException {
    return this.select(0L);
}
  • 继续调用SelectorImpl的register重载方法
public int select(long var1) throws IOException {
    if (var1 < 0L) {
        throw new IllegalArgumentException("Negative timeout");
    } else {
        //进行真正的选择
        return this.lockAndDoSelect(var1 == 0L ? -1L : var1);
    }
}
  • 继续调用SelectorImpl的lockAndDoSelect方法
private int lockAndDoSelect(long var1) throws IOException {
    //对该Selector加锁
    synchronized(this) {
        if (!this.isOpen()) {
            throw new ClosedSelectorException();
        } else {
            int var10000;
            //对publicKeys加锁
            synchronized(this.publicKeys) {
                synchronized(this.publicSelectedKeys) {
                    var10000 = this.doSelect(var1);
                }
            }

            return var10000;
        }
    }
}
  • 继续调用到WindowsSelectorImpl的doSelect方法
protected int doSelect(long var1) throws IOException {
    if (this.channelArray == null) {
        throw new ClosedSelectorException();
    } else {
        this.timeout = var1;
        //处理已经注销的SelectionKey
        this.processDeregisterQueue();
        if (this.interruptTriggered) {
            this.resetWakeupSocket();
            return 0;
        } else {
            //调整线程数
            this.adjustThreadsCount();
            //重置完成锁
            this.finishLock.reset();
            //启动线程,在windows系统中,每个线程最多轮询1024个句柄
            this.startLock.startThreads();

            try {
                //标识开始,与end方法成对使用
                this.begin();

                try {

                    //进行真正的轮询
                    this.subSelector.poll();
                } catch (IOException var7) {
                    this.finishLock.setException(var7);
                }

                if (this.threads.size() > 0) {
                    this.finishLock.waitForHelperThreads();
                }
            } finally {
                //结束
                this.end();
            }
            //检查异常
            this.finishLock.checkForException();
            //处理注销的SelectionKey
            this.processDeregisterQueue();
            //更新被选择的key
            int var3 = this.updateSelectedKeys();
            this.resetWakeupSocket();
            return var3;
        }
    }
}
 
  • processDeregisterQueue方法分析
void processDeregisterQueue() throws IOException {
    //获取selector中的canceled set
    Set var1 = this.cancelledKeys();
    //对canceledSet加锁
    synchronized(var1) {
        if (!var1.isEmpty()) {
            Iterator var3 = var1.iterator();
            //循环set中的元素
            while(var3.hasNext()) {
                SelectionKeyImpl var4 = (SelectionKeyImpl)var3.next();

                try {
                    //具体的注销实现,可以自行了解
                    this.implDereg(var4);
                } catch (SocketException var11) {
                    throw new IOException("Error deregistering key", var11);
                } finally {
                    //将SelectionKey从set中移除
                    var3.remove();
                }
            }
        }

    }
}
  • adjustThreadsCount方法分析
private void adjustThreadsCount() {
    int var1;
    //如果threadCount大于threads的size,则新建一个线程,并将线程加入到threads中,然后启动
    if (this.threadsCount > this.threads.size()) {
        for(var1 = this.threads.size(); var1 < this.threadsCount; ++var1) {
            WindowsSelectorImpl.SelectThread var2 = new WindowsSelectorImpl.SelectThread(var1);
            this.threads.add(var2);
            var2.setDaemon(true);
            var2.start();
        }
    //如果threadCount小于threds的size,则需要将threads的数目缩减到与threadCount相同。
    } else if (this.threadsCount < this.threads.size()) {
        for(var1 = this.threads.size() - 1; var1 >= this.threadsCount; --var1) {
            ((WindowsSelectorImpl.SelectThread)this.threads.remove(var1)).makeZombie();
        }
    }

}
  • this.finishLock.reset()
private void reset() {
    //设置需要完成的线程数
    this.threadsToFinish = WindowsSelectorImpl.this.threads.size();
}
  • this.startLock.startThreads方法
private synchronized void startThreads() {
    //对运行的线程数加1
    ++this.runsCounter;
    //进行广播通知
    this.notifyAll();
}
  • this.begin方法
protected final void begin() {
    if (interruptor == null) {
        interruptor = new Interruptible() {
                public void interrupt(Thread ignore) {
                    AbstractSelector.this.wakeup();
                }};
    }
    //将interruptor赋值给给Thread的Interrupbible(即blocker)
    AbstractInterruptibleChannel.blockedOn(interruptor);
    Thread me = Thread.currentThread();
    if (me.isInterrupted())
        //如果线程中断则进行中断操作
        interruptor.interrupt(me);
}
  • this.subSelector.poll()方法
private int poll() throws IOException {
    //调用native的poll0方法,重点关注readFds,writeFds,exceptFds,后续会用到
    return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}
  • this.updateSelectedKeys()方法
private int updateSelectedKeys() {
    ++this.updateCount;
    byte var1 = 0;
    //处理当前线程轮询的key
    int var4 = var1 + this.subSelector.processSelectedKeys(this.updateCount);

    WindowsSelectorImpl.SelectThread var3;
    //处理其他线程轮循到SelectionKey
    for(Iterator var2 = this.threads.iterator(); var2.hasNext(); var4 += var3.subSelector.processSelectedKeys(this.updateCount)) {
        var3 = (WindowsSelectorImpl.SelectThread)var2.next();
    }

    return var4;
}
  • this.subSelector.processSelectedKeys(...)
private int processSelectedKeys(long var1) {
    byte var3 = 0;
    //处理可读的FileDescriptor
    int var4 = var3 + this.processFDSet(var1, this.readFds, Net.POLLIN, false);
    //处理可写的FileDescriptor
    var4 += this.processFDSet(var1, this.writeFds, Net.POLLCONN | Net.POLLOUT, false);
    //处理例外的FileDescriptor
    var4 += this.processFDSet(var1, this.exceptFds, Net.POLLIN | Net.POLLCONN | Net.POLLOUT, true);
    return var4;
}

private int processFDSet(long var1, int[] var3, int var4, boolean var5) {
    int var6 = 0;

    for(int var7 = 1; var7 <= var3[0]; ++var7) {
        int var8 = var3[var7];
        if (var8 == WindowsSelectorImpl.this.wakeupSourceFd) {
            synchronized(WindowsSelectorImpl.this.interruptLock) {
                WindowsSelectorImpl.this.interruptTriggered = true;
            }
        } else {
            //根据FileDescriptor从fdMap中获取到MapEntry 
            WindowsSelectorImpl.MapEntry var9 = WindowsSelectorImpl.this.fdMap.get(var8);
            //如果MapEntry 不为空
            if (var9 != null) {
                //获取SelectionKey
                SelectionKeyImpl var10 = var9.ski;
                //!var5 或者SelectionKey所对应的channel不是SocketChannelImpl的实例或者不丢弃紧急数据
                if (!var5 || !(var10.channel() instanceof SocketChannelImpl) || !WindowsSelectorImpl.this.discardUrgentData(var8)) {
                    if (WindowsSelectorImpl.this.selectedKeys.contains(var10)) {
                        //清除数不等于更新数
                        if (var9.clearedCount != var1) {
                            //转换并设置准备好的感兴趣事件
                            if (var10.channel.translateAndSetReadyOps(var4, var10) && var9.updateCount != var1) {                               //更新更新数
                                var9.updateCount = var1;
                                ++var6;
                            }
                        } else if (var10.channel.translateAndUpdateReadyOps(var4, var10) && var9.updateCount != var1) {
                            var9.updateCount = var1;
                            ++var6;
                        }

                        var9.clearedCount = var1;
                    } else {
                        if (var9.clearedCount != var1) {
                            //转换并设置准备好的感兴趣事件
                            var10.channel.translateAndSetReadyOps(var4, var10);
                            if ((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
                                //将准备好的SelectionKey添加到selectedKeys中
                                WindowsSelectorImpl.this.selectedKeys.add(var10);
                                var9.updateCount = var1;
                                ++var6;
                            }
                        } else {
                            var10.channel.translateAndUpdateReadyOps(var4, var10);
                            if ((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
                                //将准备好的SelectionKey添加到selectedKeys中
                                WindowsSelectorImpl.this.selectedKeys.add(var10);
                                var9.updateCount = var1;
                                ++var6;
                            }
                        }

                        var9.clearedCount = var1;
                    }
                }
            }
        }
    }

    return var6;
}
  • processFDSet(...)方法的作用即是将准备好的SelectionKey放入到selectedKeys中
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值