趁还没上班,赶紧发出来,接上篇《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了。