mina3源码分析,回话过程创建(二)

1、回话过程创建

  1) NioTcpServer.bind方法,打开ServerSocketChannel,并绑定地址SocketAddress。
public synchronized void bind(SocketAddress localAddress) {
        Assert.assertNotNull(localAddress, "localAddress");

        // check if the address is already bound
        if (address != null) {
            throw new IllegalStateException("address " + address + " already bound");
        }

        LOG.info("binding address {}", localAddress);
        address = localAddress;

        try {
        //1、打开一个ServerSocketChannel并绑定注册地址
            serverChannel = ServerSocketChannel.open();
            serverChannel.socket().setReuseAddress(isReuseAddress());
            serverChannel.socket().bind(address);
            serverChannel.configureBlocking(false);
        } catch (IOException e) {
            throw new MinaRuntimeException("can't bind address" + address, e);
        }
	//2、向selectorLoop注册serverChannel
        acceptSelectorLoop.register(true, false, false, false, this, serverChannel, null);

        idleChecker = new IndexedIdleChecker();
        idleChecker.start();

        // it's the first address bound, let's fire the event
        fireServiceActivated();
    }

2)通过acceptSelectorLoop.register 方法向将channel的accept注册到selector中,具体如下

   
@Override
public void register(boolean accept, boolean connect, boolean read, boolean write, SelectorListener listener,
        SelectableChannel channel, RegistrationCallback callback) {
    if (IS_DEBUG) {
        LOG.debug("registering : {} for accept : {}, connect: {}, read : {}, write : {}, channel : {}",
                new Object[] { listener, accept, connect, read, write, channel });
    }

    int ops = 0;

    if (accept) {
        ops |= SelectionKey.OP_ACCEPT;
    }

    if (connect) {
        ops |= SelectionKey.OP_CONNECT;
    }

    if (read) {
        ops |= SelectionKey.OP_READ;
    }

    if (write) {
        ops |= SelectionKey.OP_WRITE;
    }

    // TODO : if it's the same selector/worker, we don't need to do that we could directly enqueue
    registrationQueue.add(new Registration(ops, channel, listener, callback));

    // Now, wakeup the selector in order to let it update the selectionKey status
    wakeup();
}



      3)具体是向registrationQueue队列中添加注册。然后唤醒Selector,具体处理在NioSelectorLoop的SelectorWorker类的run方法处理。如下所示:
     
@Override
    public void run() {

        for (;;) {
            try {
                
                final int readyCount = selector.select();


                if (readyCount > 0) {
                    final Iterator<SelectionKey> it = selector.selectedKeys().iterator();

                    while (it.hasNext()) {
                        final SelectionKey key = it.next();
                        final SelectorListener listener = (SelectorListener) key.attachment();
                        int ops = key.readyOps();
                        boolean isAcceptable = (ops & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
                        boolean isConnectable = (ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
                        boolean isReadable = (ops & SelectionKey.OP_READ) == SelectionKey.OP_READ;
                        boolean isWritable = (ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;
                        listener.ready(isAcceptable, isConnectable, isReadable, isReadable ? readBuffer : null,
                                isWritable);
                        // if you don't remove the event of the set, the selector will present you this event again
                        // and again
                        if (IS_DEBUG) {
                            LOG.debug("remove");
                        }

                        it.remove();
                    }
                }

                // new registration
                while (!registrationQueue.isEmpty()) {
                    final Registration reg = registrationQueue.poll();

                    try {
                        SelectionKey selectionKey = reg.channel.register(selector, reg.ops, reg.listener);

                        if (reg.getCallback() != null) {
                            reg.getCallback().done(selectionKey);
                        }
                    } catch (final ClosedChannelException ex) {
                        // dead session..
                        LOG.error("socket is already dead", ex);
                    }
                }

                // tasks
                while (!runnableQueue.isEmpty()) {
                    runnableQueue.poll().run();
                }
            } catch (final Exception e) {
                LOG.error("Unexpected exception : ", e);
            }
        }
    }
}



 如上代码,最开始并没有客户端连接,selector.select执行后的结果为0,代码直接进入
while (!registrationQueue.isEmpty()) {
在这个方法的  
reg.channel.register(selector, reg.ops, reg.listener);
处理逻辑中将ServerSocketChannel注册到Selector上面,监听的事件是accept,key的attachment为NioTcpServer。
4)客户端有连接进来,如果客户端有连接进入。则SelectorWorker类的run方法的
final int readyCount = selector.select();
的readyCount>0,  SelectionKey的操作为accept,  key的attchment即listener为NioTcpServer。NioTcpServer的ready方法将负责处理客户端的连接请求。
5)NioTcpServer的ready方法分析,具体代码如下:
@Override
    public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer,
            final boolean write) {
        if (accept) {
            LOG.debug("acceptable new client");

            // accepted connection
 
                LOG.debug("new client accepted");
                createSession(getServerSocketChannel().accept());

 如代码所示:ready的主要逻辑是接受客户端的请求,并创建Session操作。具体分析createSession的逻辑。

private synchronized void createSession(SocketChannel clientSocket) throws IOException {
      
        SocketChannel socketChannel = clientSocket;
        TcpSessionConfig config = getSessionConfig();
        SelectorLoop readWriteSelectorLoop = readWriteSelectorPool.getSelectorLoop();
        final NioTcpSession session = new NioTcpSession(this, socketChannel, readWriteSelectorLoop, idleChecker);

        socketChannel.configureBlocking(false);

        // apply idle configuration
        session.getConfig().setIdleTimeInMillis(IdleStatus.READ_IDLE, config.getIdleTimeInMillis(IdleStatus.READ_IDLE));
        session.getConfig().setIdleTimeInMillis(IdleStatus.WRITE_IDLE,
                config.getIdleTimeInMillis(IdleStatus.WRITE_IDLE));

        // apply the default service socket configuration
        Boolean keepAlive = config.isKeepAlive();

        ...

        Boolean tcpNoDelay = config.isTcpNoDelay();

        if (tcpNoDelay != null) {
            session.getConfig().setTcpNoDelay(tcpNoDelay);
        }

        ......

        // add the session to the queue for being added to the selector
        readWriteSelectorLoop.register(false, false, true, false, session, socketChannel, new RegistrationCallback() {

            @Override
            public void done(SelectionKey selectionKey) {
                session.setSelectionKey(selectionKey);
                session.setConnected();
            }
        });

        idleChecker.sessionRead(session, System.currentTimeMillis());
        idleChecker.sessionWritten(session, System.currentTimeMillis());
    }

创建回话的主要逻辑是设置Session相关的数据, 设置完成后,如下代码
readWriteSelectorLoop.register(false, false, true, false, session, socketChannel, new RegistrationCallback() {


            @Override
            public void done(SelectionKey selectionKey) {
                session.setSelectionKey(selectionKey);
                session.setConnected();
            }
        });
将channel添加到selector上,注册事件为read操作,同时设置SelectionKey的attchment为 NioTcpSession。后续读写相关的处理,将由NioTcpSession的ready方法处理。
@Override
    public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer,
            final boolean write) {
        
        if (connect) {
           ....        }

        if (read) {
            processRead(readBuffer);
        }

        if (write) {
            processWrite(selectorLoop);
        }
        if (accept) {
            throw new IllegalStateException("accept event should never occur on NioTcpSession");
        }
    }


关键点:我们将看到处理客户端链接使用的是 acceptSelectorLoop,处理用户Session的读写使用的是readWriteSelectorLoop,这样来说对于客户端的链接和Session之间的读写使用不同Selector处理。
5)readWriteSelectorLoop.register方法的末尾参数传入了匿名内部类,在done方法中回调Session的setConnected方法。

void setConnected() {
        if (!isCreated()) {
            throw new RuntimeException("Trying to open a non created session");
        }

        state = SessionState.CONNECTED;

        if (connectFuture != null) {
            connectFuture.complete(this);
            // free some memory
            connectFuture = null;
        }

        processSessionOpen();
    }
该方法首先设置session的状态为已连接,然后调用processSessionOpen()方法,processSessionOpen()方法调用了父类的processSessionOpen()方法。具体如下:

 public void processSessionOpen() {
        if (IS_DEBUG) {
            LOG.debug("processing session open event");
        }

        try {

            for (IoFilter filter : chain) {
                filter.sessionOpened(this);
            }

            IoHandler handler = getService().getIoHandler();

            if (handler != null) {
                IoHandlerExecutor executor = getService().getIoHandlerExecutor();

                if (executor != null) {
                    // asynchronous event
                    executor.execute(new OpenEvent(this));
                } else {
                    // synchronous call (in the I/O loop)
                    handler.sessionOpened(this);
                }
            }
        } catch (RuntimeException e) {
            processException(e);
        }
    }
该方法首先循环调用filters的sessionOpened方法处理,处理完成后获取处理业务逻辑的IoHandler,调用IoHandler的sessionOpened方法进行处理。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值