上一篇写的是IoAcceptor是服务器端的接收代码,今天要写的是IoConnector,是客户端的连接器。在昨天,我们还留下一些问题没有解决,这些问题今天同样会产生,但是都要等到讲到session的时候才能逐步揭开。先回顾一下问题:
l 我们已经在AbstractPollingIoAcceptor中看到了,mina是将连接(命令)和业务(读写)分不同线程处理的,但是我们还没有看到mina是如何实现对这些线程的管理的。
l 在昨天的最后,我们看到在NioSocketAcceptor中的具体NIO实现,但是我们还没有看到mina是在哪里调用了这些具体操作的。当然这也是mina对连接线程管理的一部分。
这些问题今天也会出现,因为从名字上就能看出,IoConnector和IoAcceptor的构造相差不大,所以在写connector的分析时,主要会从结构和差异上入手,最后再给出昨天没写完的删减版的Acceptor。先回顾一下客户端连接的代码:
02 |
IoConnector
connector = new NioSocketConnector(); |
04 |
connector.setConnectTimeout(30000); |
06 |
connector.getFilterChain().addLast( |
08 |
new ProtocolCodecFilter(new MessageCodecFactory( |
09 |
new InfoMessageDecoder(Charset.forName("utf-8")), |
10 |
new InfoMessageEncoder(Charset.forName("utf-8"))))); |
12 |
connector.setHandler(new ClientHandler()); |
13 |
IoSession
session = null; |
15 |
ConnectFuture
future = connector.connect(new InetSocketAddress( |
17 |
future.awaitUninterruptibly();//
等待连接创建完成 |
18 |
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过程是让子类去实现的简单看下代码:
01 |
public final ConnectFuture
connect(SocketAddress remoteAddress, SocketAddress localAddress, |
02 |
IoSessionInitializer<? extends ConnectFuture>
sessionInitializer) { |
04 |
throw new IllegalStateException("The
connector has been disposed."); |
07 |
if (remoteAddress
== null)
{ |
08 |
throw new IllegalArgumentException("remoteAddress"); |
11 |
if (!getTransportMetadata().getAddressType().isAssignableFrom(remoteAddress.getClass()))
{ |
12 |
throw new IllegalArgumentException("remoteAddress
type: " +
remoteAddress.getClass() + "
(expected: " |
13 |
+
getTransportMetadata().getAddressType() + ")"); |
16 |
if (localAddress
!= null &&
!getTransportMetadata().getAddressType().isAssignableFrom(localAddress.getClass())) { |
17 |
throw new IllegalArgumentException("localAddress
type: " +
localAddress.getClass() + "
(expected: " |
18 |
+
getTransportMetadata().getAddressType() + ")"); |
21 |
if (getHandler()
== null)
{ |
22 |
if (getSessionConfig().isUseReadOperation())
{ |
23 |
setHandler(new IoHandler()
{ |
24 |
public void exceptionCaught(IoSession
session, Throwable cause) throws Exception
{ |
28 |
public void messageReceived(IoSession
session, Object message) throws Exception
{ |
32 |
public void messageSent(IoSession
session, Object message) throws Exception
{ |
36 |
public void sessionClosed(IoSession
session) throws Exception
{ |
40 |
public void sessionCreated(IoSession
session) throws Exception
{ |
44 |
public void sessionIdle(IoSession
session, IdleStatus status) throws Exception
{ |
48 |
public void sessionOpened(IoSession
session) throws Exception
{ |
53 |
throw new IllegalStateException("handler
is not set."); |
57 |
return connect0(remoteAddress,
localAddress, sessionInitializer); |
61 |
protected abstract ConnectFuture
connect0(SocketAddress remoteAddress, SocketAddress localAddress, |
62 |
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一样:
01 |
private volatile Selector
selector; |
04 |
protected SocketChannel
newHandle(SocketAddress localAddress) throws Exception
{ |
05 |
SocketChannel
ch = SocketChannel.open(); |
07 |
int receiveBufferSize
= (getSessionConfig()).getReceiveBufferSize(); |
08 |
if (receiveBufferSize
> 65535)
{ |
09 |
ch.socket().setReceiveBufferSize(receiveBufferSize); |
12 |
if (localAddress
!= null)
{ |
13 |
ch.socket().bind(localAddress); |
15 |
ch.configureBlocking(false); |
只是需要注意,这里面专门有个内部类来处理selectionkey,将遍历的过程都抽离出来了,这个和我们用NIO的一般写法稍有不同,这样做的好处也是为了复用:
01 |
private static class SocketChannelIterator implements Iterator<SocketChannel>
{ |
03 |
private final Iterator<SelectionKey>
i; |
05 |
private SocketChannelIterator(Collection<SelectionKey>
selectedKeys) { |
06 |
this.i
= selectedKeys.iterator(); |
12 |
public boolean hasNext()
{ |
19 |
public SocketChannel
next() { |
20 |
SelectionKey
key = i.next(); |
21 |
return (SocketChannel)
key.channel(); |
27 |
public void remove()
{ |
---------------------------------------------------------
补一个上篇就应该发的acceptor的阉割版,写这样的东西主要还是为了理清楚结构。我主要是把内容简化了,但是结构都没有变,核心的成员变量也没有少:
起点IoService:
01 |
package org.apache.mina.core.rewrite.service; |
04 |
*
IO Service --handler/processor/acceptor/connector |
09 |
public interface IoService
{ |
11 |
void addListener(IoServiceListener
listener); |
14 |
void dispose(boolean awaitTermination); |
17 |
IoHandler
getHandler(); |
19 |
void setHandler(IoHandler
handler); |
22 |
int getManagedSessionCount(); |
左边的路
01 |
package org.apache.mina.core.rewrite.service; |
03 |
import java.io.IOException; |
04 |
import java.net.SocketAddress; |
10 |
*
Acceptor 主要用于:Accepts incoming connection, communicates with clients, and |
11 |
*
fires events to IoHandler |
15 |
public interface IoAcceptor extends IoService
{ |
17 |
SocketAddress
getLocalAddress(); |
19 |
Set<SocketAddress>
getLocalAddresses(); |
21 |
void bind(SocketAddress
localAddress) throws IOException; |
23 |
void bind(Iterable<? extends SocketAddress>
localAddresses) throws IOException; |
25 |
void unbind(SocketAddress
localAddress); |
28 |
/**没有写到IoSession
所以暂时不用*/ |
29 |
//IoSession
newSession(SocketAddress remoteAddress,SocketAddress localAddress); |
SocketAcceptor:
01 |
package org.apache.mina.rewrite.transport.socket; |
03 |
import java.net.InetSocketAddress; |
05 |
import org.apache.mina.core.rewrite.service.IoAcceptor; |
07 |
public interface SocketAcceptor extends IoAcceptor
{ |
09 |
InetSocketAddress
getLocalAddress(); |
11 |
void setDefaultLocalAddress(InetSocketAddress
localAddress); |
13 |
public boolean isReuseAddress(); |
17 |
//
SocketSessionConfig getSessionConfig(); |
再看右边的
001 |
package org.apache.mina.core.rewrite.service; |
003 |
import java.util.concurrent.Executor; |
004 |
import java.util.concurrent.ExecutorService; |
005 |
import java.util.concurrent.Executors; |
006 |
import java.util.concurrent.TimeUnit; |
007 |
import java.util.concurrent.atomic.AtomicInteger; |
009 |
public abstract class AbstractIoService implements IoService
{ |
011 |
private static final AtomicInteger
id = new AtomicInteger(); |
013 |
private final String
threadName; |
015 |
private final Executor
executor; |
017 |
private final boolean createdExecutor; |
019 |
private IoHandler
handler; |
022 |
protected final Object
disposalLock = new Object(); |
024 |
private volatile boolean disposing; |
026 |
private volatile boolean disposed; |
031 |
*
sessionConfig IoSessionConfig |
033 |
*
used for handling execution of IO event. can be null |
035 |
protected AbstractIoService(Object
param, Executor executor) { |
037 |
//
TODO listener & session config |
039 |
if (executor
== null)
{ |
040 |
this.executor
= Executors.newCachedThreadPool(); |
041 |
createdExecutor
= true; |
043 |
this.executor
= executor; |
044 |
createdExecutor
= false; |
047 |
threadName
= getClass().getSimpleName() + "-" +
id.incrementAndGet(); |
051 |
public void addListener(IoServiceListener
listener) { |
055 |
/**注意这个不是override来的*/ |
056 |
protected final void ececuteWorker(Runnable
worker, String suffix){ |
058 |
String
actualThreadName=threadName; |
060 |
actualThreadName=actualThreadName+"-"+suffix; |
062 |
executor.execute(worker); |
066 |
public void dispose(boolean awaitTermination)
{ |
071 |
synchronized (disposalLock)
{ |
077 |
} catch (Exception
e) { |
083 |
if (createdExecutor)
{ |
084 |
ExecutorService
e = (ExecutorService) executor; |
087 |
if (awaitTermination)
{ |
090 |
e.awaitTermination(Integer.MAX_VALUE,
TimeUnit.SECONDS); |
092 |
} catch (InterruptedException
e1) { |
094 |
Thread.currentThread().interrupt(); |
101 |
protected abstract void dispose0() throws Exception; |
104 |
public IoHandler
getHandler() { |
109 |
public void setHandler(IoHandler
handler) { |
110 |
if (handler
== null)
{ |
111 |
throw new IllegalArgumentException("handler
cannot be null"); |
113 |
//
TODO isActive: when service is active, cannot be set handler |
115 |
throw new IllegalStateException("when
service is active, cannot be set handler"); |
118 |
this.handler
= handler; |
- Mina源码阅读笔记(四)—Mina的连接IoConnector2
接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...
- Mina源码阅读笔记(一)-整体解读
今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者类的方法.我倒是想从mina源码的结构和功能上对这个框架进行剖析.源码的阅 ...
- Mina源码阅读笔记(二)- IoBuffer的封装
在阅读IoBuffer源码之前,我们先看Mina对IoBuffer的描述:A byte buffer used by MINA applications. This is a replacement ...
- Mina源码阅读笔记(七)—Mina的拦截器FilterChain
Filter我们很熟悉,在Mina中,filter chain的用法也类似于Servlet的filters,这种拦截器的设计思想能够狠轻松的帮助我们实现对资源的统一处理.我们先大致连接下mina中的f ...
- Mina源码阅读笔记(三)-Mina的连接IoAccpetor
其实在mina的源码中,IoService可以总结成五部分service责任.Processor线程处理.handler处理器.接收器和连接器,分别对应着IoService.IoProcessor.I ...
- Mina源码阅读笔记(六)—Mina异步IO的实现IoFuture
IoFuture是和IoSession紧密相连的一个类,在官网上并没有对它的描述,因为它一般不会显示的拿出来用,权当是一个工具类被session所使用.当然在作用上,这个系列可并不简单,我们先看源码的 ...
- Mina源码阅读笔记(五)—Mina对连接的操作IoSession
IoSession是Mina管理两端的一个重要部分,也是Mina的核心,Session具有了生命周期的概念,它的生命周期和连接时紧密相关的,这点在后面的介绍中会涉及.另外,好像hibernate中也有 ...
- Werkzeug源码阅读笔记(四)
今天主要讲一下werkzeug中的routing模块.这个模块是werkzeug中的重点模块,Flask中的路由相关的操作使用的都是这个模块 routing模块的用法 在讲解模块的源码之前,先讲讲这个 ...
- 源码阅读笔记 - 1 MSVC2015中的std::sort
大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...
随机推荐
- Linux之dmesg命令
功能说明:显示内核缓冲区系统控制信息的工具 ,比如系统在启动时的信息会写到/var/log/中.语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存 ...
- iOS 10 推送全解析,注意事项
本文旨在对 iOS 推送进行一个完整的剖析,如果你之前对推送一无所知,那么在你认真地阅读了全文后必将变成一个推送老手,你将会对其中的各种细节和原理有充分的理解.以下是 pikacode 使用 iOS ...
- UNIX网络编程——解决TCP网络传输“粘包”问题
当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API).TCP/IP传输层有两个并列的协议:TCP和UDP.其中TCP(transport contro ...
- JRE System Library [JavaSE-1.7](unbound)
window > preferences > java > Install jars >如果没有jdk1.7 ,点击下面的search,会自动找到已经安装对jdk1.7,选择, ...
- 04 AutoCompleteTextView
作用:输入部分文字跳处下拉框列出相应的条目 <pre name="code" class="html"> <!-- 当文本框出现两个字符才开始 ...
- (一〇九)UIButton的使用技巧 -imageView、titleLabel、圆角等
UIButton是一个常用控件,使用方法十分基本,但是有很多技巧常常不被注意,本文主要介绍UIButton的一些较高级技巧,用于实现图片和标签显示的美观性等. 开发时常常碰到按钮的下侧或者右侧有标题的 ...
- 《java入门第一季》之集合框架TreeSet存储元素自然排序以及图解
这一篇对TreeSet做介绍,先看一个简单的例子: * TreeSet:能够对元素按照某种规则进行排序. * 排序有两种方式 * A:自然排序: 从小到大排序 * B:比较器排序 Comp ...
- 用SpriteBuilder简化"耕牛遍地走"的动画效果(四)
写到这突然有童鞋质疑,你这哪里是牛,分明是熊嘛! 仔细看了下,还真像牛.反正是这个意思.怪本猫猪牛熊不分,好在道理是一样的. 下面继续,言归正传. 添加一个空白的touchBegan方法,如果没有这个 ...
- python类:描述器Descriptors和元类MetaClasses
http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制 ...
- 【linux学习笔记】Sublime Text3支持GB2312和GBK编码以及中文输入法
几天在ubuntu15.10下使用Sublime Text3发现中文乱码,以及不能使用中文输入法(搜狗输入法linux版)的问题,捣鼓了半天,终于完善了,下面po一下我的解决方案. 一.支持GB231 ...