(四)Mina源码解析之IoSession

本篇文章主要剖析IoSession的在mina中的作用,IoSession的继承结构图如下:


在Mina中所有的连接都被封装成了IoSession(关于Mina是如何将连接封装成IoSession的,请参见Mina源码解析之IoService),所有的Session的信息都存放到一个叫IoServiceListenerSupport的类中,这里是IoSession和IoServiceListener(这是一个监听器,这里我们暂且不用管和监听器有关的东西,后面会有专门的文章来讲解)的大本营,该类的部分代码如下:

public class IoServiceListenerSupport {
    /** The {@link IoService} that this instance manages. */
    private final IoService service;

    /** 所有的监听器都存放到这里. */
    private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener>();

    /**所有的session都放到这个map中 */
    private final ConcurrentMap<Long, IoSession> managedSessions = new ConcurrentHashMap<Long, IoSession>(); //①

    /**  只读的session */
    private final Map<Long, IoSession> readOnlyManagedSessions = Collections.unmodifiableMap(managedSessions);

    private final AtomicBoolean activated = new AtomicBoolean();

    /** listenerSupport最近一次被激活的时间 */
    private volatile long activationTime;

    /** listenerSupport创建以后session连接的峰值 */
    private volatile int largestManagedSessionCount = 0;

    /** A global counter to count the number of sessions managed since the start */
    private AtomicLong cumulativeManagedSessionCount = new AtomicLong(0);
代码1处存放着Server端管理的所有session,AbstractIoService的属性listeners就是一个IoServiceListenerSupport对象,当创建session时,server端的mina会调用IoServiceListenerSupport的fireSessionCreated方法,该方法的代码如下:

public void fireSessionCreated(IoSession session) {
        boolean firstSession = false;

        if (session.getService() instanceof IoConnector) {
            synchronized (managedSessions) {
                firstSession = managedSessions.isEmpty();
            }
        }

        // If already registered, ignore.
        if (managedSessions.putIfAbsent(session.getId(), session) != null) {//①这里会将新建立的session放到managedSessions属性中
            return;
        }

        // If the first connector session, fire a virtual service activation event.
        if (firstSession) {
            fireServiceActivated();
        }

        // Fire session events.
        IoFilterChain filterChain = session.getFilterChain();
        filterChain.fireSessionCreated(); //这里调用过滤器链的sessionCreated方法
        filterChain.fireSessionOpened();//这里调用过滤器链的sessionOpened方法

        int managedSessionCount = managedSessions.size();

        if (managedSessionCount > largestManagedSessionCount) {
            largestManagedSessionCount = managedSessionCount;//更新session连接的峰值数
        }

        cumulativeManagedSessionCount.incrementAndGet();

        // Fire listener events.
        for (IoServiceListener l : listeners) {
            try {
                l.sessionCreated(session);
            } catch (Exception e) {
                ExceptionMonitor.getInstance().exceptionCaught(e);
            }
        }
    }
当session连接断开时会调用IoServiceListenerSupport类的fireSessionDestroyed方法,该方法会将session从managedSessions属性中移除,这里就不列出代码了,请同学们自己查看源代码,session在哪里管理我们已经知道了,但是每个session连接都会有很多信息,比如当前session的ID、从这个session总共接收了多少个字节的信息、session上次读写信息是在什么时候等等,这些信息都保持在哪里呢?答案是在AbstractIoSession中(因为这些属性都是session的固有特性,所以存在这里),由于属性太多就不一一列出了,那么这些属性都是什么时候更新的呢,当然是涉及到它们的特性发生变化的时候了,举例来说,sessionId属性是唯一标示一个session的,那么这个属性一定是创建session的时候设置的,readBytes属性记录读取的字节数,那么这个属性就是接收数据时更新的,这写信息的管理都很简单,这里就不一一列出了,如果感兴趣的同学可以看看AbstractIoSession的源代码。上面说了这么多都是再讲session的管理。session的固有特性,但是对于session来说最重要的都不是这些,对于session来说最重要的是读写数据,下面就一起来看看session中是如何读写数据的

一旦连接建立成功后,数据的读写都是在process线程中进行管理的(如果想了解详细的细节可以看Mina源码解析之IoService),在AbstractPollingIoProcessor类中有个read(S)方法,该方法就是专门负责读取数据用的,代码如下:

private void read(S session) {
        IoSessionConfig config = session.getConfig();
        int bufferSize = config.getReadBufferSize();
        IoBuffer buf = IoBuffer.allocate(bufferSize);

        final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();//对于socket连接来说这个值为true

        try {
            int readBytes = 0;
            int ret;

            try {
                if (hasFragmentation) {

                    while ((ret = read(session, buf)) > 0) {
                        readBytes += ret;

                        if (!buf.hasRemaining()) {
                            break;
                        }
                    }
                } else {
                    ret = read(session, buf);

                    if (ret > 0) {
                        readBytes = ret;
                    }
                }
            } finally {
                buf.flip();
            }

            if (readBytes > 0) {
                IoFilterChain filterChain = session.getFilterChain();
                filterChain.fireMessageReceived(buf);
                buf = null;

                if (hasFragmentation) {
                    if (readBytes << 1 < config.getReadBufferSize()) {
                        session.decreaseReadBufferSize();  //①  动态减少数据缓冲区ReadBufferSize
                    } else if (readBytes == config.getReadBufferSize()) {
                        session.increaseReadBufferSize();//② 动态增加数据缓存区ReadBufferSize
                    }
                }
            }

            if (ret < 0) {
                // scheduleRemove(session);
                IoFilterChain filterChain = session.getFilterChain();
                filterChain.fireInputClosed();
            }
        } catch (Exception e) {
            if (e instanceof IOException) {
                if (!(e instanceof PortUnreachableException)
                        || !AbstractDatagramSessionConfig.class.isAssignableFrom(config.getClass())
                        || ((AbstractDatagramSessionConfig) config).isCloseOnPortUnreachable()) {
                    scheduleRemove(session);
                }
            }

            IoFilterChain filterChain = session.getFilterChain();
            filterChain.fireExceptionCaught(e);
        }
    }
其实这就是一段用nio来读取数据的方法,如果对nio还不太熟的同学建议去补习一下nio的知识,这里主要说两个问题,第一如果启动readBufferSize设置的很小或者不设置其实都没有关系,因为在代码1、 2处会根据设置的或者默认的readBufferSize和接收的字节数的大小进行比较来进行动态调整。第二这里接收的数据都是二进制的数据,一般情况下我们的应用都不会直接处理二进制的数据的,所以我们要将接收的数据进行转换,在mina中我们使用编解码器来完成数据的转换,包括数据的粘包、断包等问题的处理都是在编解码器中解决的,关于编辑码器的源码剖析会在下一篇文章中进行。










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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值