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
- mina3源码分析,回话过程创建(二)
- mina3源码分析,启动过程分析(一)
- 【Zookeeper源码二】Zookeeper 客户端创建连接过程分析
- Qemu-KVM虚拟机初始化及创建过程源码简要分析(二)
- nova创建虚拟机过程源码简要分析(一)
- Nginx源码分析-启动初始化过程(二)
- curl源码分析(二)协议注册与使用过程
- Nginx源码分析-启动初始化过程(二)
- 【Java】【Flume】Flume-NG启动过程源码分析(二)
- 【Java】【Flume】Flume-NG启动过程源码分析(二)
- PDF阅读器系列之--MuPDF源码分析过程(二)
- PDF阅读器系列之--MuPDF源码分析过程(二)
- elasticsearch源码分析之启动过程(二)
- springMVC源码分析--HandlerInterceptor拦截器调用过程(二)
- springMVC源码分析--HandlerInterceptor拦截器调用过程(二)
- elasticsearch源码分析之启动过程(二)
- (二)UI绘制流程-绘制过程源码分析
- Spring Boot启动过程源码分析(二)事件监听器
- 题目1368:二叉树中和为某一值的路径
- mac osx系统中python的libsvm配置
- LeetCoder 3. Longest Substring Without Repeating Characters
- 行距和对齐方式
- uboot的环境变量
- mina3源码分析,回话过程创建(二)
- STM8入门资源
- LBP特征 旋转不变 标准型 圆型 及其在人脸识别中的应用含代码
- formValidator辅助函数
- 常见的查找
- datetime.datetime.strptime:万能的日期格式转换
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
- 用 webpack 实现持久化缓存
- JQuery 取消时间冒泡