上一篇写的是IoAcceptor是服务器端的接收代码,今天要写的是IoConnector,是客户端的连接器。在昨天,我们还留下一些问题没有解决,这些问题今天同样会产生,但是都要等到讲到session的时候才能逐步揭开。先回顾一下问题:
l 我们已经在AbstractPollingIoAcceptor中看到了,mina是将连接(命令)和业务(读写)分不同线程处理的,但是我们还没有看到mina是如何实现对这些线程的管理的。
l 在昨天的最后,我们看到在NioSocketAcceptor中的具体NIO实现,但是我们还没有看到mina是在哪里调用了这些具体操作的。当然这也是mina对连接线程管理的一部分。
这些问题今天也会出现,因为从名字上就能看出,IoConnector和IoAcceptor的构造相差不大,所以在写connector的分析时,主要会从结构和差异上入手,最后再给出昨天没写完的删减版的Acceptor。先回顾一下客户端连接的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 创建一个非阻塞的客户端程序
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过程是让子类去实现的简单看下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
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一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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的一般写法稍有不同,这样做的好处也是为了复用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
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;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
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
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
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) {
}
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
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了。