Mina源码阅读笔记(四)—Mina的连接IoConnector

趁还没上班,赶紧发出来,接上篇《IoAcceptor》

上一篇写的是IoAcceptor是服务器端的接收代码,今天要写的是IoConnector,是客户端的连接器。在昨天,我们还留下一些问题没有解决,这些问题今天同样会产生,但是都要等到讲到session的时候才能逐步揭开。先回顾一下问题:

l  我们已经在AbstractPollingIoAcceptor中看到了,mina是将连接(命令)和业务(读写)分不同线程处理的,但是我们还没有看到mina是如何实现对这些线程的管理的。

l  在昨天的最后,我们看到在NioSocketAcceptor中的具体NIO实现,但是我们还没有看到mina是在哪里调用了这些具体操作的。当然这也是mina对连接线程管理的一部分。

这些问题今天也会出现,因为从名字上就能看出,IoConnector和IoAcceptor的构造相差不大,所以在写connector的分析时,主要会从结构和差异上入手,最后再给出昨天没写完的删减版的Acceptor。先回顾一下客户端连接的代码:

// 创建一个非阻塞的客户端程序
		IoConnector connector = new NioSocketConnector();
		// 设置链接超时时间
		connector.setConnectTimeout(30000);
		// 添加过滤器
		connector.getFilterChain().addLast(
				"codec",
				new ProtocolCodecFilter(new MessageCodecFactory(
						new InfoMessageDecoder(Charset.forName("utf-8")),
						new InfoMessageEncoder(Charset.forName("utf-8")))));
		// 添加业务逻辑处理器类
		connector.setHandler(new ClientHandler());
		IoSession session = null;
		try {
			ConnectFuture future = connector.connect(new InetSocketAddress(
					HOST, PORT));// 创建连接
			future.awaitUninterruptibly();// 等待连接创建完成
			session = future.getSession();// 获得session

还是先看IoConnector的结构图,图来自mina官网,用XMind绘制的。在写构成之前,我们还是先看一下mina官网对这些connector的介绍:

As we have to use an IoAcceptor for servers, you have to implement the IoConnector. Again, we have many implementation classes :

  • NioSocketConnector : the non-blocking Socket transport Connector
  • NioDatagramConnector : the non-blocking UDP transport * Connector*
  • AprSocketConnector : the blocking Socket transport * Connector*, based on APR
  • ProxyConnector : a Connector providing proxy support
  • SerialConnector : a Connector for a serial transport
  • VmPipeConnector : the in-VM * Connector*

其中,NioSocketConnector是我们最常用到的,proxy方式虽然在mina的源码中也花了大篇幅去撰写,但可惜的是很少有相关的文档,所以学习的成本还挺高的。今天我们主要还是按照上图画的两条路来看NioSocketConnector。

和昨天一样,我们还是从左边的路走起,看interface IoConnector,这个接口主要定义了连接的方法以及socket连接时用到的参数。在mina中通过IoFuture来描述、侦听在IoSession上实现的异步IO操作,所以这IoConnector中的connect方法都返回了一个ConnectFuture实例。

而在SocketConnector接口中的定义中就显得更简单了,它和IoConnector之间也是接口的继承关系,在SocketConnector中就定义了两类方法,一个对远程地址的get和set,一个拿到session的配置。这些都容易理解。

再来看右边,AbstractIoConnector,这个抽象类主要作用就是实现IoConnector里定义的操作,至于他又继承了AbstractIoService,一是为了用到父类(AbstractIoService)的方法,二是为了将那些父类没有实现的方法继续传递下去,让它(AbstractIoConnector)的子类去实现。所以看多了,好多结构也能看明白了,这里我觉得主要要学习的还是接口、抽象类之间的引用关系。

继续看AbstractIoConnector,这个类主要是实现了connect的逻辑操作(封装了连接前后的一些必要执行步骤和check一些状态),具体的连接操作还是让子类去实现,这个和上篇写的AbstractIoAcceptor一模一样,在AbstractIoAcceptor中,主要也是封装了bind的逻辑操作,真正的bind过程是让子类去实现的简单看下代码:

public final ConnectFuture connect(SocketAddress remoteAddress, SocketAddress localAddress,
            IoSessionInitializer<? extends ConnectFuture> sessionInitializer) {
        if (isDisposing()) {
            throw new IllegalStateException("The connector has been disposed.");
        }

        if (remoteAddress == null) {
            throw new IllegalArgumentException("remoteAddress");
        }

        if (!getTransportMetadata().getAddressType().isAssignableFrom(remoteAddress.getClass())) {
            throw new IllegalArgumentException("remoteAddress type: " + remoteAddress.getClass() + " (expected: "
                    + getTransportMetadata().getAddressType() + ")");
        }

        if (localAddress != null && !getTransportMetadata().getAddressType().isAssignableFrom(localAddress.getClass())) {
            throw new IllegalArgumentException("localAddress type: " + localAddress.getClass() + " (expected: "
                    + getTransportMetadata().getAddressType() + ")");
        }

        if (getHandler() == null) {
            if (getSessionConfig().isUseReadOperation()) {
                setHandler(new IoHandler() {
                    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
                        // Empty handler
                    }

                    public void messageReceived(IoSession session, Object message) throws Exception {
                        // Empty handler
                    }

                    public void messageSent(IoSession session, Object message) throws Exception {
                        // Empty handler
                    }

                    public void sessionClosed(IoSession session) throws Exception {
                        // Empty handler
                    }

                    public void sessionCreated(IoSession session) throws Exception {
                        // Empty handler
                    }

                    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
                        // Empty handler
                    }

                    public void sessionOpened(IoSession session) throws Exception {
                        // Empty handler
                    }
                });
            } else {
                throw new IllegalStateException("handler is not set.");
            }
        }

        return connect0(remoteAddress, localAddress, sessionInitializer);
}


   protected abstract ConnectFuture connect0(SocketAddress remoteAddress, SocketAddress localAddress,
            IoSessionInitializer<? extends ConnectFuture> sessionInitializer);

Connect0才是最后具体的操作,而这一步操作在这个类中被没有给出实现。具体实现放在了AbstractPollingIoConnector上。和昨天一样,这些设计都是对称的,我们还是看三点:

l  implementing client transport using a polling strategy

l  A Executor will be used for running client connection, and an AbstractPollingIoProcessor will be used for processing connected client I/O operations like reading, writing and closing.

l  All the low level methods for binding, connecting, closing need to be provided by the subclassing implementation

至于内部的具体实现,那跟acceptor中没什么区别,连使用的工具类都差别不大,这部分就很容易读懂了,只不过一个是bind一个是connect。

最后我们看NioSocketConnector,具体连接的实现类,只有一个成员变量和NioSocketAcceptor一样:

private volatile Selector selector;

   @Override
    protected SocketChannel newHandle(SocketAddress localAddress) throws Exception {
        SocketChannel ch = SocketChannel.open();

        int receiveBufferSize = (getSessionConfig()).getReceiveBufferSize();
        if (receiveBufferSize > 65535) {
            ch.socket().setReceiveBufferSize(receiveBufferSize);
        }

        if (localAddress != null) {
            ch.socket().bind(localAddress);
        }
        ch.configureBlocking(false);
        return ch;
    }

只是需要注意,这里面专门有个内部类来处理selectionkey,将遍历的过程都抽离出来了,这个和我们用NIO的一般写法稍有不同,这样做的好处也是为了复用:

private static class SocketChannelIterator implements Iterator<SocketChannel> {

        private final Iterator<SelectionKey> i;

        private SocketChannelIterator(Collection<SelectionKey> selectedKeys) {
            this.i = selectedKeys.iterator();
        }

        /**
         * {@inheritDoc}
         */
        public boolean hasNext() {
            return i.hasNext();
        }

        /**
         * {@inheritDoc}
         */
        public SocketChannel next() {
            SelectionKey key = i.next();
            return (SocketChannel) key.channel();
        }

        /**
         * {@inheritDoc}
         */
        public void remove() {
            i.remove();
        }
    }
---------------------------------------------------------

补一个上篇就应该发的acceptor的阉割版,写这样的东西主要还是为了理清楚结构。我主要是把内容简化了,但是结构都没有变,核心的成员变量也没有少:

起点IoService:

package org.apache.mina.core.rewrite.service;

/**
 * IO Service --handler/processor/acceptor/connector
 * 
 * @author ChenHui
 * 
 */
public interface IoService {
	/** 添加listener */
	void addListener(IoServiceListener listener);

	/** 销毁 */
	void dispose(boolean awaitTermination);

	/** 设置handler */
	IoHandler getHandler();

	void setHandler(IoHandler handler);

	/** 管理session */
	int getManagedSessionCount();
	
	boolean isActive();
}
左边的路
package org.apache.mina.core.rewrite.service;

import java.io.IOException;
import java.net.SocketAddress;
import java.util.Set;

/**
 * 注意接口的继承,这里的方法都是新定义的
 * 	 
 * Acceptor 主要用于:Accepts incoming connection, communicates with clients, and
 * fires events to IoHandler
 * 
 * @author ChenHui
 */
public interface IoAcceptor extends IoService {
	
	SocketAddress getLocalAddress();
	
	Set<SocketAddress> getLocalAddresses();
	
	void bind(SocketAddress localAddress) throws IOException;
	
	void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException;
	
	void unbind(SocketAddress localAddress);
	
	
	/**没有写到IoSession 所以暂时不用*/
	//IoSession newSession(SocketAddress remoteAddress,SocketAddress localAddress);
}
SocketAcceptor:
package org.apache.mina.rewrite.transport.socket;

import java.net.InetSocketAddress;

import org.apache.mina.core.rewrite.service.IoAcceptor;

public interface SocketAcceptor extends IoAcceptor {

	InetSocketAddress getLocalAddress();

	void setDefaultLocalAddress(InetSocketAddress localAddress);

	public boolean isReuseAddress();
	
	// ...

	// SocketSessionConfig getSessionConfig();
}
再看右边的
package org.apache.mina.core.rewrite.service;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class AbstractIoService implements IoService {

	private static final AtomicInteger id = new AtomicInteger();

	private final String threadName;

	private final Executor executor;

	private final boolean createdExecutor;

	private IoHandler handler;

	// 用于安全的关闭
	protected final Object disposalLock = new Object();

	private volatile boolean disposing;

	private volatile boolean disposed;

	/**
	 * 
	 * @param param
	 *            sessionConfig IoSessionConfig
	 * @param executor
	 *            used for handling execution of IO event. can be null
	 */
	protected AbstractIoService(Object param, Executor executor) {

		// TODO listener & session config

		if (executor == null) {
			this.executor = Executors.newCachedThreadPool();
			createdExecutor = true;
		} else {
			this.executor = executor;
			createdExecutor = false;
		}

		threadName = getClass().getSimpleName() + "-" + id.incrementAndGet();
	}
	
	@Override
	public void addListener(IoServiceListener listener) {
		// TODO add listener
	}

	/**注意这个不是override来的*/
	protected final void ececuteWorker(Runnable worker, String suffix){
		
		String actualThreadName=threadName;
		if(suffix!=null){
			actualThreadName=actualThreadName+"-"+suffix;
		}
		executor.execute(worker);
	}
	
	@Override
	public void dispose(boolean awaitTermination) {
		if (disposed) {
			return;
		}

		synchronized (disposalLock) {
			if (!disposing) {
				disposing = true;
				try {
					/** 真正的关闭方法TODO */
					dispose0();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		if (createdExecutor) {
			ExecutorService e = (ExecutorService) executor;
			e.shutdown();

			if (awaitTermination) {
				try {

					e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);

				} catch (InterruptedException e1) {
					// 注意异常时的中断处理
					Thread.currentThread().interrupt();
				}
			}
		}
		disposed = true;
	}

	protected abstract void dispose0() throws Exception;

	@Override
	public IoHandler getHandler() {
		return this.handler;
	}

	@Override
	public void setHandler(IoHandler handler) {
		if (handler == null) {
			throw new IllegalArgumentException("handler cannot be null");
		}
		// TODO isActive: when service is active, cannot be set handler
		if(isActive()){
			throw new IllegalStateException("when service is active, cannot be set handler");
		}
		
		this.handler = handler;
	}

}
AbstractIoAcceptor:
package org.apache.mina.core.rewrite.service;

import java.io.IOException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

public abstract class AbstractIoAcceptor extends AbstractIoService implements
		IoAcceptor {

	private final List<SocketAddress> defaultLocalAddresses = new ArrayList<SocketAddress>();

	private final List<SocketAddress> unmodifiableDeffaultLocalAddresses = Collections
			.unmodifiableList(defaultLocalAddresses);

	private final Set<SocketAddress> boundAddresses = new HashSet<SocketAddress>();

	private boolean disconnectOnUnbind = true;

	/** 这里不是很明白,为什么要用protected 而 不是private */
	protected final Object bindLock = new Object();

	/**
	 * 注意这个构造方法是一定要写的,否则编译不通过:抽象类继承时候,构造方法都要写,而且必须包含super
	 * 
	 * @param param
	 *            sessionConfig
	 * @param executor
	 */
	protected AbstractIoAcceptor(Object param, Executor executor) {
		super(param, executor);
		defaultLocalAddresses.add(null);
	}

	@Override
	public SocketAddress getLocalAddress() {

		Set<SocketAddress> localAddresses = getLocalAddresses();
		if (localAddresses.isEmpty()) {
			return null;
		}
		return localAddresses.iterator().next();
	}

	@Override
	public final Set<SocketAddress> getLocalAddresses() {
		Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
		synchronized (boundAddresses) {
			localAddresses.addAll(boundAddresses);
		}
		return localAddresses;
	}

	@Override
	public void bind(SocketAddress localAddress) throws IOException {
		// TODO Auto-generated method stub

	}

	@Override
	public void bind(Iterable<? extends SocketAddress> localAddresses)
			throws IOException {
		// TODO isDisposing()

		if (localAddresses == null) {
			throw new IllegalArgumentException("localAddresses");
		}

		List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();

		for (SocketAddress a : localAddresses) {
			// TODO check address type
			localAddressesCopy.add(a);
		}

		if (localAddressesCopy.isEmpty()) {
			throw new IllegalArgumentException("localAddresses is empty");
		}

		boolean active = false;

		synchronized (bindLock) {
			synchronized (boundAddresses) {
				if (boundAddresses.isEmpty()) {
					active = true;
				}
			}
		}
		/** implement in abstractIoService */
		if (getHandler() == null) {
			throw new IllegalArgumentException("handler is not set");
		}

		try {
			Set<SocketAddress> addresses = bindInternal(localAddressesCopy);

			synchronized (boundAddresses) {
				boundAddresses.addAll(addresses);
			}
		} catch (IOException e) {
			throw e;
		} catch (RuntimeException e) {
			throw e;
		} catch (Throwable e) {
			throw new RuntimeException("Filed ti bind");
		}
		
		if(active){
			//do sth
		}
	}

	protected abstract Set<SocketAddress> bindInternal(
			List<? extends SocketAddress> localAddress) throws Exception;

	@Override
	public void unbind(SocketAddress localAddress) {
		// TODO Auto-generated method stub
		
	}
}
polling:
package org.apache.mina.core.rewrite.polling;

import java.net.SocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.mina.core.rewrite.service.AbstractIoAcceptor;

public abstract class AbstractPollingIoAcceptor extends AbstractIoAcceptor {

	private final Semaphore lock = new Semaphore(1);

	private volatile boolean selectable;

	private AtomicReference<Acceptor> acceptorRef = new AtomicReference<Acceptor>();

	/**
	 * define the num of sockets that can wait to be accepted.
	 */
	protected int backlog = 50;

	/**
	 * 一样的,这个构造方法也要写
	 * 
	 * @param param
	 * @param executor
	 */
	protected AbstractPollingIoAcceptor(Object param, Executor executor) {
		super(param, executor);
		// TODO Auto-generated constructor stub
	}

	/**
	 * init the polling system. will be called at construction time
	 * 
	 * @throws Exception
	 */
	protected abstract void init() throws Exception;

	protected abstract void destory() throws Exception;

	protected abstract int select() throws Exception;
	/**这里有点儿变动*/
	protected abstract ServerSocketChannel open(SocketAddress localAddress) throws Exception;

	@Override
	protected Set<SocketAddress> bindInternal(
			List<? extends SocketAddress> localAddress) throws Exception {
		// ...
		try {
			lock.acquire();
			Thread.sleep(10);
		} finally {
			lock.release();
		}
		// ...
		return null;
	}

	/**
	 * this class is called by startupAcceptor() method it's a thread accepting
	 * incoming connections from client
	 * 
	 * @author ChenHui
	 * 
	 */
	private class Acceptor implements Runnable {
		@Override
		public void run() {
			assert (acceptorRef.get() == this);

			int nHandles = 0;

			lock.release();

			while (selectable) {
				try {
					int selected = select();

					// nHandles+=registerHandles();

					if (nHandles == 0) {
						acceptorRef.set(null);
						// ...
					}
				} catch (Exception e) {

				}
			}
		}
	}
}
好了最后看NioSoeketAcceptor:
package org.apache.mina.rewrite.transport.socket.nio;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.concurrent.Executor;

import org.apache.mina.core.rewrite.polling.AbstractPollingIoAcceptor;
import org.apache.mina.rewrite.transport.socket.SocketAcceptor;

public final class NioSocketAcceptor extends AbstractPollingIoAcceptor
		implements SocketAcceptor {

	private volatile Selector selector;

	protected NioSocketAcceptor(Object param, Executor executor) {
		super(param, executor);
		// TODO Auto-generated constructor stub
	}

	@Override
	public int getManagedSessionCount() {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * 这个方法继承自AbstractIoAcceptor
	 * 
	 * The type NioSocketAcceptor must implement the inherited abstract method
	 * SocketAcceptor.getLocalAddress() to override
	 * AbstractIoAcceptor.getLocalAddress()
	 */
	@Override
	public InetSocketAddress getLocalAddress() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setDefaultLocalAddress(InetSocketAddress localAddress) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean isReuseAddress() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	protected void init() throws Exception {
		selector = Selector.open();
	}

	@Override
	protected void destory() throws Exception {
		if (selector != null) {
			selector.close();
		}
	}

	@Override
	protected int select() throws Exception {
		return selector.select();
	}

	@Override
	protected void dispose0() throws Exception {
		// TODO Auto-generated method stub

	}

	protected ServerSocketChannel open(SocketAddress localAddress)
			throws Exception {
		ServerSocketChannel channel =ServerSocketChannel.open();
		
		boolean success=false;
		
		try{
			channel.configureBlocking(false);
			
			ServerSocket socket=channel.socket();
			
			socket.setReuseAddress(isReuseAddress());
			
			socket.bind(localAddress);
			
			channel.register(selector, SelectionKey.OP_ACCEPT);
			
			success=true;
		}finally{
			if(!success){
				//close(channel);
			}
		}
		return channel;
	}

	@Override
	public boolean isActive() {
		// TODO Auto-generated method stub
		return false;
	}

}
------------------------------------------------------

到此为止将连接部分都写完了,在连接部分还有些零碎的东西,比如handler、polling,这些都只是稍稍提了一下,具体后面会在介绍其他部分是肯定还会碰上,我还是想把重心放在最主要的部分去写,下一篇应该要写到session了。


转载于:https://my.oschina.net/ielts0909/blog/91193

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值