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

来源:互联网 发布:知乎怎么发图片文章 编辑:程序博客网 时间:2024/05/21 07:58

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中,具体如下

   
@Overridepublic 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