TimeClient序列图
TimeClient
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/**
* Created by liqiushi on 2017/12/12.
*/
public class TimeClient implements Runnable {
private String host;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop;
public TimeClient(String host, int port) {
this.host = host == null ? "127.0.0.1" : host;
this.port = port;
try {
selector = selector.open();
socketChannel = SocketChannel.open();
//channel要设置成非阻塞
socketChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
doconnect();
} catch (IOException e) {
e.printStackTrace();
}
while (!stop) {
try {
selector.select(1000);
Set selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
handleInput(selectionKey);
}
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void doconnect() throws IOException {
if (socketChannel.connect(new InetSocketAddress(host, port))) {
socketChannel.register(selector, SelectionKey.OP_READ);
dowrite(socketChannel);
} else {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
}
private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
if (key.isConnectable()) {
if (socketChannel.finishConnect()) {
try {
socketChannel.register(selector, SelectionKey.OP_READ);
dowrite(socketChannel);
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.exit(1);
}
}
if (key.isReadable()) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = socketChannel.read(readBuffer);
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("Now : " + body);
this.stop = true;
} else if (readBytes < 0) {
//对端链路关闭
key.cancel();
socketChannel.close();
}
}
}
}
private void dowrite(SocketChannel socketChannel) throws IOException {
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
String request = "QUERY TIME ORDER";
byte[] bytes = request.getBytes();
writeBuffer.put(bytes);
writeBuffer.flip();
socketChannel.write(writeBuffer);
if (!writeBuffer.hasRemaining()) {
System.out.println("Send order 2 server succeed.");
}
}
public static void main(String[] args) {
TimeClient timeClient = new TimeClient("127.0.0.1", 8001);
new Thread(timeClient).start();
}
}
疑问
isConnectable()和finishConnect()
if(key.isConnectable()){
try {
socketChannel.finishConnect();
socketChannel.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
在非阻塞模式下,SocketChannel的connect()会直接返回,不会由于io出错或直到连接成功才返回(阻塞情况下),而isConnectable()代表可以连接,接收到了服务端的SYN-ACK,属于第二次握手,此时正处于连接中,需要调用finishConnect()对连接进行确认。