[MINA2.0源码](五)客户端写数据——FilterChain

客户端session初始化完后,就可以写数据了:session.write("Hello,Server!I'm Client.");

AbstractIoSession

    public WriteFuture write(Object message) {
        return write(message, null);
    }
转调
    public WriteFuture write(Object message, SocketAddress remoteAddress) {
        if (message == null) {
            throw new IllegalArgumentException("Trying to write a null message : not allowed");
        }

        // We can't send a message to a connected session if we don't have
        // the remote address
        if (!getTransportMetadata().isConnectionless() && (remoteAddress != null)) {
            throw new UnsupportedOperationException();
        }

        // If the session has been closed or is closing, we can't either
        // send a message to the remote side. We generate a future
        // containing an exception.
        if (isClosing() || !isConnected()) {
            WriteFuture future = new DefaultWriteFuture(this);
            WriteRequest request = new DefaultWriteRequest(message, future, remoteAddress);
            WriteException writeException = new WriteToClosedSessionException(request);
            future.setException(writeException);
            return future;
        }

        FileChannel openedFileChannel = null;

        // TODO: remove this code as soon as we use InputStream
        // instead of Object for the message.
        try {
            if ((message instanceof IoBuffer) && !((IoBuffer) message).hasRemaining()) {
                // Nothing to write : probably an error in the user code
                throw new IllegalArgumentException("message is empty. Forgot to call flip()?");
            } else if (message instanceof FileChannel) {
                FileChannel fileChannel = (FileChannel) message;
                message = new DefaultFileRegion(fileChannel, 0, fileChannel.size());
            } else if (message instanceof File) {
                File file = (File) message;
                openedFileChannel = new FileInputStream(file).getChannel();
                message = new FilenameFileRegion(file, openedFileChannel, 0, openedFileChannel.size());
            }
        } catch (IOException e) {
            ExceptionMonitor.getInstance().exceptionCaught(e);
            return DefaultWriteFuture.newNotWrittenFuture(this, e);
        }

        // Now, we can write the message. First, create a future
        WriteFuture writeFuture = new DefaultWriteFuture(this);
        WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);

        // Then, get the chain and inject the WriteRequest into it
        IoFilterChain filterChain = getFilterChain();
        filterChain.fireFilterWrite(writeRequest);

        // TODO : This is not our business ! The caller has created a
        // FileChannel,
        // he has to close it !
        if (openedFileChannel != null) {
            // If we opened a FileChannel, it needs to be closed when the write
            // has completed
            final FileChannel finalChannel = openedFileChannel;
            writeFuture.addListener(new IoFutureListener<WriteFuture>() {
                public void operationComplete(WriteFuture future) {
                    try {
                        finalChannel.close();
                    } catch (IOException e) {
                        ExceptionMonitor.getInstance().exceptionCaught(e);
                    }
                }
            });
        }

        // Return the WriteFuture.
        return writeFuture;
    }
将Session、发送的数据message封装成WriteFuture和WriteRequest对象。

再调用过滤器链filterChain对发送的数据进行过滤。

一、过滤器链是如何创建的?

IoFilterChain filterChain = getFilterChain();NioSession类

    public IoFilterChain getFilterChain() {
        return filterChain;
    }
1、filterChain在其构造方法实例化的DefaultIoFilterChain类
    protected NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel) {
        super(service);
        this.channel = channel;
        this.processor = processor;
        filterChain = new DefaultIoFilterChain(this);
    }
DefaultIoFilterChain类构造方法
    public DefaultIoFilterChain(AbstractIoSession session) {
        if (session == null) {
            throw new IllegalArgumentException("session");
        }

        this.session = session;
        head = new EntryImpl(null, null, "head", new HeadFilter());
        tail = new EntryImpl(head, null, "tail", new TailFilter());
        head.nextEntry = tail;
    }
EntryImpl是DefaultIoFilterChain的内部类。通过其构造方法就可以知道:保存着某个过滤器,及其前后过滤器。

private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter)

默认创建了过滤器链的首位、末尾过滤器,用户添加的过滤器将按顺序被添加到这两个过滤器中间。

2、回到例子代码中,有这句

		acceptor.getFilterChain().addLast("codec", filter);  
这个filter是IoFilter子类ProtocolCodecFilter的对象。

NioSocketSession中有filterChain保存的是过滤器链,而NioSocketAcceptor有filterChainBuilder是为了创建session中的filterChain。

这个filterChainBuilder也是在代码中可以配置的,如果没配置则默认创建一个DefaultIoFilterChainBuilder类的对象。

acceptor.getFilterChain()也是返回其字段filterChainBuilder(必须为DefaultIoFilterChainBuilder类对象)和sesssion的同名方法返回的对象不一样。

DefaultIoFilterChainBuilder.addLast()

    public synchronized void addLast(String name, IoFilter filter) {
        register(entries.size(), new EntryImpl(name, filter));
    }
register
    private void register(int index, Entry e) {
        if (contains(e.getName())) {
            throw new IllegalArgumentException("Other filter is using the same name: " + e.getName());
        }

        entries.add(index, e);
    }
entries是DefaultIoFilterChainBuilder的字段,为CopyOnWriteArrayList类的对象。

先将业务定义的所有过滤器按顺序添加到entries中。

3、等到初始化session时,AbstractPollingIoProcessor中的addNow()方法中,

            IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
            chainBuilder.buildFilterChain(session.getFilterChain());
session.getService()方法返回的是NioSocketAcceptor对象,chainBuilder就是其字段filterChainBuilder——DefaultIoFilterChainBuilder对象

DefaultIoFilterChainBuilder.buildFilterChain()

    public void buildFilterChain(IoFilterChain chain) throws Exception {
        for (Entry e : entries) {
            chain.addLast(e.getName(), e.getFilter());
        }
    }
这里就往session的filterChain中不断的添加,之前保存在entries字段中的过滤器。

DefaultIoFilterChain.addLast();

    public synchronized void addLast(String name, IoFilter filter) {
        checkAddable(name);
        register(tail.prevEntry, name, filter);
    }
register()
    private void register(EntryImpl prevEntry, String name, IoFilter filter) {
        EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);

        try {
            filter.onPreAdd(this, name, newEntry.getNextFilter());
        } catch (Exception e) {
            throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':' + filter + " in " + getSession(), e);
        }

        prevEntry.nextEntry.prevEntry = newEntry;
        prevEntry.nextEntry = newEntry;
        name2entry.put(name, newEntry);

        try {
            filter.onPostAdd(this, name, newEntry.getNextFilter());
        } catch (Exception e) {
            deregister0(newEntry);
            throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':' + filter + " in " + getSession(), e);
        }
    }
这里就是,将需要添加的filter添加到末位过滤器前。并且保存到name2entry中,以备下次判断是否添加过了。另外再改变改filter前后,都会调用其相应的方法,做些业务上的处理。

二、如何执行过滤器链?

filterChain.fireFilterWrite(writeRequest);

DefaultIoFilterChain.fireFilterWrite()

    public void fireFilterWrite(WriteRequest writeRequest) {
        Entry tail = this.tail;
        callPreviousFilterWrite(tail, session, writeRequest);
    }
从过滤器链的末位过滤器开始往前执行。
    private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {
        try {
            IoFilter filter = entry.getFilter();
            NextFilter nextFilter = entry.getNextFilter();
            filter.filterWrite(nextFilter, session, writeRequest);
        } catch (Throwable e) {
            writeRequest.getFuture().setException(e);
            fireExceptionCaught(e);
        }
    }

entry.getNextFilter();返回的是EntryImpl内部匿名类NextFilter的对象(EntryImpl是DefaultIoFilterChain的内部类)

filter.filterWrite(nextFilter, session, writeRequest);

    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
        nextFilter.filterWrite(session, writeRequest);
    }
NextFilter.filterWrite

    public void filterWrite(IoSession session, WriteRequest writeRequest) {
        Entry nextEntry = EntryImpl.this.prevEntry;
        callPreviousFilterWrite(nextEntry, session, writeRequest);
    }
prevEntry就是tail的上一个节点,再迭代上一个过滤器的filterWrite方法。有内部类时,EntryImpl.this表示内部类对象,如果要取外部类的对象:外部类名.this即可。
而我们自己定义的过滤器,其中必定有一条语句调用上一个过滤器的方法:nextFilter.filterWrite(session, writeRequest);不然这个链就断了。

最后会执行到HeadFilter

    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {

        AbstractIoSession s = (AbstractIoSession) session;

        // Maintain counters.
        if (writeRequest.getMessage() instanceof IoBuffer) {
            IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
            // I/O processor implementation will call buffer.reset()
            // it after the write operation is finished, because
            // the buffer will be specified with messageSent event.
            buffer.mark();
            int remaining = buffer.remaining();

            if (remaining == 0) {
                // Zero-sized buffer means the internal message
                // delimiter.
                s.increaseScheduledWriteMessages();
            } else {
                s.increaseScheduledWriteBytes(remaining);
            }
        } else {
            s.increaseScheduledWriteMessages();
        }

        WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();

        if (!s.isWriteSuspended()) {
            if (writeRequestQueue.size() == 0) {
                // We can write directly the message
                s.getProcessor().write(s, writeRequest);
            } else {
                s.getWriteRequestQueue().offer(s, writeRequest);
                s.getProcessor().flush(s);
            }
        } else {
            s.getWriteRequestQueue().offer(s, writeRequest);
        }
    }
1、如果写入的参数是IoBuffer类型。则需要标记Buffer中剩下空间是多少,这在后面往通道中写数据会用到。

2、WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();

这是值时在初始化session是设值的。NioSocketConnector父类AbstractPollingIoConnector.initSession方法中

				            ((AbstractIoSession) session).setWriteRequestQueue(session.getService().getSessionDataStructureFactory()
				                    .getWriteRequestQueue(session));
值为DefaultIoSessionDataStructureFactory内部类DefaultWriteRequestQueue对象。DefaultWriteRequestQueue又是封装了ConcurrentLinkedQueue的。

3、s.isWriteSuspended()返回false,只要不在业务代码中调用session的suspendWrite方法设值暂停写操作,都会返回false。

4、判断writeRequestQueue保存了多少写操作的请求。刚开始为0,执行s.getProcessor().write(s, writeRequest);

    public void write(S session, WriteRequest writeRequest) {
        WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();

        writeRequestQueue.offer(session, writeRequest);

        if (!session.isWriteSuspended()) {
            this.flush(session);
        }
    }
将这条写请求保存到writeRequestQueue中,如果下次再有写请求进来就会执行。
                s.getWriteRequestQueue().offer(s, writeRequest);
                s.getProcessor().flush(s);
不管writeRequestQueue中的写请求多少都会先将其压入队中,writeRequestQueue.offer()方法。
        public synchronized void offer(IoSession session, WriteRequest writeRequest) {
            q.offer(writeRequest);
        }
session这个参数没用到了

再调用SimpleIoProcessorPool的flush方法。

5、SimpleIoProcessorPool.flush

    public final void flush(S session) {
        getProcessor(session).flush(session);
    }

转调NioProcessor父类AbstractPollingIoProcessor的flush()方法,将session添加到flushingSessions中。

    public final void flush(S session) {
        // add the session to the queue if it's not already
        // in the queue, then wake up the select()
        if (session.setScheduledForFlush(true)) {
            flushingSessions.add(session);
            wakeup();
        }
    }
wakeup是唤醒,AbstractPollingIoProcessor内部类Processor的select阻塞。

6、Processor.run()中,前面部分都是读数据的操作,因为不是读请求,所以都不会执行。直到flush(currentTime);

    private void flush(long currentTime) {
        if (flushingSessions.isEmpty()) {
            return;
        }

        do {
            S session = flushingSessions.poll(); // the same one with firstSession

            if (session == null) {
                // Just in case ... It should not happen.
                break;
            }

            // Reset the Schedule for flush flag for this session,
            // as we are flushing it now
            session.unscheduledForFlush();

            SessionState state = getState(session);

            switch (state) {
            case OPENED:
                try {
                    boolean flushedAll = flushNow(session, currentTime);

                    if (flushedAll && !session.getWriteRequestQueue().isEmpty(session)
                            && !session.isScheduledForFlush()) {
                        scheduleFlush(session);
                    }
                } catch (Exception e) {
                    scheduleRemove(session);
                    IoFilterChain filterChain = session.getFilterChain();
                    filterChain.fireExceptionCaught(e);
                }

                break;

            case CLOSING:
                // Skip if the channel is already closed.
                break;

            case OPENING:
                // Retry later if session is not yet fully initialized.
                // (In case that Session.write() is called before addSession()
                // is processed)
                scheduleFlush(session);
                return;

            default:
                throw new IllegalStateException(String.valueOf(state));
            }

        } while (!flushingSessions.isEmpty());
    }
遍历flushingSessions队列,逐个写,flushNow()
    private boolean flushNow(S session, long currentTime) {
        if (!session.isConnected()) {
            scheduleRemove(session);
            return false;
        }

        final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();

        final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();

        // Set limitation for the number of written bytes for read-write
        // fairness. I used maxReadBufferSize * 3 / 2, which yields best
        // performance in my experience while not breaking fairness much.
        final int maxWrittenBytes = session.getConfig().getMaxReadBufferSize()
                + (session.getConfig().getMaxReadBufferSize() >>> 1);
        int writtenBytes = 0;
        WriteRequest req = null;

        try {
            // Clear OP_WRITE
            setInterestedInWrite(session, false);

            do {
                // Check for pending writes.
                req = session.getCurrentWriteRequest();

                if (req == null) {
                    req = writeRequestQueue.poll(session);

                    if (req == null) {
                        break;
                    }

                    session.setCurrentWriteRequest(req);
                }

                int localWrittenBytes = 0;
                Object message = req.getMessage();

                if (message instanceof IoBuffer) {
                    localWrittenBytes = writeBuffer(session, req, hasFragmentation, maxWrittenBytes - writtenBytes,
                            currentTime);

                    if ((localWrittenBytes > 0) && ((IoBuffer) message).hasRemaining()) {
                        // the buffer isn't empty, we re-interest it in writing
                        writtenBytes += localWrittenBytes;
                        setInterestedInWrite(session, true);
                        return false;
                    }
                } else if (message instanceof FileRegion) {
                    localWrittenBytes = writeFile(session, req, hasFragmentation, maxWrittenBytes - writtenBytes,
                            currentTime);

                    // Fix for Java bug on Linux
                    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988
                    // If there's still data to be written in the FileRegion,
                    // return 0 indicating that we need
                    // to pause until writing may resume.
                    if ((localWrittenBytes > 0) && (((FileRegion) message).getRemainingBytes() > 0)) {
                        writtenBytes += localWrittenBytes;
                        setInterestedInWrite(session, true);
                        return false;
                    }
                } else {
                    throw new IllegalStateException("Don't know how to handle message of type '"
                            + message.getClass().getName() + "'.  Are you missing a protocol encoder?");
                }

                if (localWrittenBytes == 0) {
                    // Kernel buffer is full.
                    setInterestedInWrite(session, true);
                    return false;
                }

                writtenBytes += localWrittenBytes;

                if (writtenBytes >= maxWrittenBytes) {
                    // Wrote too much
                    scheduleFlush(session);
                    return false;
                }
            } while (writtenBytes < maxWrittenBytes);
        } catch (Exception e) {
            if (req != null) {
                req.getFuture().setException(e);
            }

            IoFilterChain filterChain = session.getFilterChain();
            filterChain.fireExceptionCaught(e);
            return false;
        }

        return true;
    }

先清除session上的OP_WRITE,等写完后再注册监听OP_WRITE。localWrittenBytes = writeBuffer(session, req, hasFragmentation, maxWrittenBytes - writtenBytes,currentTime);

    private int writeBuffer(S session, WriteRequest req, boolean hasFragmentation, int maxLength, long currentTime)
    throws Exception {
		IoBuffer buf = (IoBuffer) req.getMessage();
		int localWrittenBytes = 0;
		
		if (buf.hasRemaining()) {
		    int length;
		
		    if (hasFragmentation) {
		        length = Math.min(buf.remaining(), maxLength);
		    } else {
		        length = buf.remaining();
		    }
		
		    try {
		        localWrittenBytes = write(session, buf, length);
		    } catch (IOException ioe) {
		        // We have had an issue while trying to send data to the 
		        // peer : let's close the session.
		        session.close(true);
		    }
		
		}
		
		session.increaseWrittenBytes(localWrittenBytes, currentTime);
		
		if (!buf.hasRemaining() || (!hasFragmentation && (localWrittenBytes != 0))) {
		    // Buffer has been sent, clear the current request.
		    int pos = buf.position();
		    buf.reset();
		
		    fireMessageSent(session, req);
		
		    // And set it back to its position
		    buf.position(pos);
		}
		
		return localWrittenBytes;
	}
localWrittenBytes = write(session, buf, length);在其子类NioProcessor中

    protected int write(NioSession session, IoBuffer buf, int length) throws Exception {
        if (buf.remaining() <= length) {
            return session.getChannel().write(buf.buf());
        }

        int oldLimit = buf.limit();
        buf.limit(buf.position() + length);
        try {
            return session.getChannel().write(buf.buf());
        } finally {
            buf.limit(oldLimit);
        }
    }
往channel中写数据buf,如果buf超过了最大限制的话,那么就会写入最大限制的数据,然后将这个session加到flushingSessions队中,接着写这些这个buf。直到下完为止。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值