SocketChannel 使用open或connect/fin
2021-03-01 本文已影响0人
tracy_668
使用connect效果
首先给出一个通用的服务端代码:
package NonBlocking;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class TestOpenAndConnectServer {
static ServerSocketChannel serverSocketChannel = null;
static Selector selector = null;
public static void main(String[] args) throws IOException {
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8888));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
int result = 0; int i = 1;
while ((result = selector.select()) > 0) {
System.out.println(String.format("selector %dth loop, ready event number is %d", i++, result));
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey sk = iterator.next();
if (sk.isAcceptable()) {
ServerSocketChannel ss = (ServerSocketChannel)sk.channel();
SocketChannel socketChannel = ss.accept();
socketChannel.configureBlocking(false); //也切换非阻塞
socketChannel.register(selector, SelectionKey.OP_READ); //注册read事件
System.out.println("接受到新的客户端连接");
} else if (sk.isReadable()) {
System.out.println("有数据可读");
}
iterator.remove();
}
}
}
}
使用connect效果
package NonBlocking;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
public class TestOpenAndConnectClient {
static SocketChannel socketChannel = null;
static Selector selector = null;
public static void main(String[] args) throws IOException {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
boolean connected = socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
System.out.println(connected);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
int result = 0; int i = 1;
while((result = selector.select()) > 0) {
System.out.println(String.format("selector %dth loop, ready event number is %d", i++, result));
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey sk = iterator.next();
if (sk.isConnectable()) {
SocketChannel sc = (SocketChannel)sk.channel();
if (sc.finishConnect()) {
System.out.println("与服务端建立连接成功");
} else {
System.out.println("与服务端建立连接失败");
}
sc.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {
System.out.println("有数据可读");
}
iterator.remove();
}
}
}
}
在服务端已经运行后,再运行客户端,效果如下:
false
selector 1th loop, ready event number is 1
与服务端建立连接成功
如果服务端没有运行,再运行客户端,效果如下:可见在执行sc.finishConnect()抛出了异常。
false
selector 1th loop, ready event number is 1
Exception in thread "main" java.net.ConnectException: Connection refused: no further information
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
at NonBlocking.TestOpenAndConnectClient.main(TestOpenAndConnectClient.java:34)
正如connect的api文档所解释:
If this channel is in non-blocking mode then an invocation of this method initiates a non-blocking connection operation. If the connection is established immediately, as can happen with a local connection, then this method returns true. Otherwise this method returns false and the connection operation must later be completed by invoking the finishConnect method.
一个channel在非阻塞模式下执行connect后,如果连接能马上建立好则返回true,否则完成false。如果返回false,那么只能通过之后调用finishConnect来判断连接是否完成。
finishConnect的api文档:
If the connection operation failed then invoking this method will cause an appropriate IOException to be thrown.
如果连接操作已经失败了,那么将会抛出异常。
If this channel is in non-blocking mode then this method will return false if the connection process is not yet complete.
如果连接操作还未完成好,那么将返回false。
但由于我们是通过Selector来触发事件,所以connect事件到来时,这个连接要么已经成功,要么已经失败(抛出异常的形式告诉我们)。
就算是目标地址不可连接 在connect不会抛异常,只会在fininshConnect抛异常