SelectionKey的其他几个方法,attach(Object)为key设置附件,并返回之前的附件;interestOps()和readyOps()返回关注状态和当前状态;cancel()为取消注册;isValid()表示key是否有效(在key取消注册,通道关闭,选择器关闭这三个事情发生之前,key均为有效的,但不包括对方关闭通道,所以读写应注意异常)。
还有一个状态上面没有使用,OP_CONNECT这个主要是用于客户端,对应的key的方法是isConnectable()表示已经创建好了连接。
非阻塞实现的客户端如下:
01
Selector selector = Selector.open();
02
// 创建一个套接字通道,注意这里必须使用无参形式
03
SocketChannel channel = SocketChannel.open();
04
// 设置为非阻塞模式,这个方法必须在实际连接之前调用(所以open的时候不能提供服务器地址,否则会自动连接)
05
channel.configureBlocking(false);
06
// 连接服务器,由于是非阻塞模式,这个方法会发起连接请求,并直接返回false(阻塞模式是一直等到链接成功并返回是否成功)
07
channel.connect(new InetSocketAddress("127.0.0.1", 7777));
08
// 注册关联链接状态
09
channel.register(selector, SelectionKey.OP_CONNECT);
10
while (true) {
11
// 前略 和服务器端的类似
12
// ...
13
// 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道
14
Set
15
for (SelectionKey key : keys) {
16
// OP_CONNECT 两种情况,链接成功或失败这个方法都会返回true
17
if (key.isConnectable()) {
18
// 由于非阻塞模式,connect只管发起连接请求,finishConnect()方法会阻塞到链接结束并返回是否成功
19
// 另外还有一个isConnectionPending()返回的是是否处于正在连接状态(还在三次握手中)
20
if (channel.finishConnect()) {
21
// 链接成功了可以做一些自己的处理,略
22
// ...
23
// 处理完后必须吧OP_CONNECT关注去掉,改为关注OP_READ
24
key.interestOps(SelectionKey.OP_READ);
25
}
26
}
27
// 后略 和服务器端的类似
28
// ...
29
}
30
}
虽然例子是这样的,不过服务器和客户端可以自己单方面选择是否采用非阻塞模式,用阻塞模式的客户端连接非阻塞模式的服务器端是OK的。
二 NIO2的异步IO通道
以下API是由Java7提供。老版本无法使用。
异步IO通道的实现有两种实现方式,一是在阻塞模式的原方法(主要指的是read和write,具体可以查看API文档)上传于一个CompletionHandler实例以实现回调,另外也可以令其返回一个Future实例(Java5新增同步工具包java.util.concurrent中的API),然后再适当的时候通过其get方法来获取返回的结果。异步文件I/O通道为AsynchronousFileChannel,而异步套接字通道为AsynchronousServerSocketChannel,分别对应其各自的原始通道。
异步I/O需要与一个AsynchronousChannelGroup对象关联,他实质上就是一个用于I/O的线程池。AsynchronousChannelGroup对象可以通过其自身静态方法的withThreadPool(),withCachedThreadPool(),withFixedThreadPool()提供一个线程池来创建(线程池也是Java5新增同步工具包java.util.concurrent中的API)。在异步通道创建open()时,可将这个对象传入进行关联。如果没有提供这个对象的话,就默认使用系统分组。但是需要注意的是系统分组的线程池是个守护线程池,JVM是可能在没有读写完成前正常结束的。AsynchronousChannelGroup在使用完后需要shutdowm(),这方面和线程池的关闭是类似的。
作者:逝水fox