异步连接需要做以下操作:
- 设置socket为非阻塞模式
- 注册OP_CONNECT事件
- OP_CONNECT事件发生时,调用SocketChannel的finishConnection
代码如下:
public class Client1 {
Selector selector;
int writeEventTrigerCount = 0;
public void run() throws IOException {
selector = Selector.open();
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
boolean connectImmediately = sc.connect(new InetSocketAddress("182.61.200.6", 80));
if (!connectImmediately) {
System.out.println("connect inprogress");
sc.register(selector, SelectionKey.OP_CONNECT);
} else {
System.out.println("connect immediately");
sc.register(selector, SelectionKey.OP_WRITE);
}
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys){
if (!selectionKey.isValid()) {
System.out.println(""+selectionKey+" is not valid");
} else if (selectionKey.isConnectable()) {
System.out.println(""+selectionKey+" connected");
SocketChannel client = (SocketChannel)selectionKey.channel();
client.finishConnect();
selectionKey.interestOps(SelectionKey.OP_WRITE);
} else if (selectionKey.isWritable()) {
System.out.println(""+selectionKey+" writable");
if (++writeEventTrigerCount > 10) {
selectionKey.interestOps(SelectionKey.OP_READ);
}
} else if (selectionKey.isReadable()) {
//System.out.println(""+selectionKey+" readable");
}
}
}
}
public static void main(String args[]) {
Client1 client = new Client1();
try {
client.run();
} catch (IOException e) {
e.printStackTrace();
}
}
}
连接失败会发生什么?
我们修改下上面的代码,将
sc.connect(new InetSocketAddress("182.61.200.6", 80));
改成
sc.connect(new InetSocketAddress("192.168.2.1", 80));
注意要确保主机"192.168.2.1"是不可达的。
运行代码,等待几秒钟,会出现下面内容:
sun.nio.ch.SelectionKeyImpl@1d81eb93 connected
java.net.ConnectException: Connection timed out: no further information
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source)
at com.github.ralgond.nioexample.ClientConnectToUnreachableHost.run(ClientConnectToUnreachableHost.java:31)
at com.github.ralgond.nioexample.ClientConnectToUnreachableHost.main(ClientConnectToUnreachableHost.java:50)
我们看到结果的第一行以及第四行,可以得到一个结论:不管连接成功还是失败,selector都会报isConnectable事件,然而至于成功还是失败,需要调用SocketChannel的finishConnect来判断。