packagesocket;importjava.io.IOException;importjava.net.InetSocketAddress;importjava.net.ServerSocket;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.Iterator;importjava.util.Set;public classNIOServer2 {private void startServer() throwsIOException {
Selector selector=Selector.open();
{
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.configureBlocking(false);
ServerSocket ss=ssc.socket();
InetSocketAddress address= new InetSocketAddress(9000);
ss.bind(address);
System.out.println("ssc 0 : " +ssc);
System.out.println("ss 0 : " +ss);
SelectionKey acceptKey=ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("acceptKey: " +acceptKey);
printKeyInfo(acceptKey);
System.out.println("Going to listen on 9000");
}while (true) {
System.out.println("===================================\nstart select...");int num =selector.select();
System.out.println("NIOServer: Number of keys after select operation: " +num);
Set selectionKeys =selector.selectedKeys();
Iterator it =selectionKeys.iterator();while(it.hasNext()) {
SelectionKey key=it.next();
System.out.println("key: " +key);
printKeyInfo(key);
it.remove();if ((key.readyOps() & SelectionKey.OP_ACCEPT) ==SelectionKey.OP_ACCEPT) {
System.out.println("select ACCEPT");
ServerSocketChannel ssc=(ServerSocketChannel) key.channel();
SocketChannel sc=ssc.accept();
sc.configureBlocking(false);
System.out.println("ssc 1 : " +ssc);
System.out.println("sc 1 : " +sc);
SelectionKey newKey=sc.register(selector, SelectionKey.OP_READ);
System.out.println("new key:" +newKey);
printKeyInfo(newKey);
}else if ((key.readyOps() & SelectionKey.OP_READ) ==SelectionKey.OP_READ) {//System.out.println("select READ");//System.out.print("before cancel:");printKeyInfo(key);//key.cancel();//System.out.println("after cancel:");printKeyInfo(key);
SocketChannel sc =(SocketChannel) key.channel();
System.out.println("sc 2 : " +sc);//echo data//下面的处理是正确的,count<0则cancel key。count=0则进入下一轮select()阻塞等待数据。//try {//int count = doRead(key);//if (count < 0) {//key.cancel();//System.out.println("cancel key for < 0");//sc.read(ByteBuffer.allocate(2));//}//} catch(IOException e) {//e.printStackTrace();//key.cancel();//System.out.println("cancel key");//}//下面的处理过程是错误的,偶然情况下会出现正确逻辑。在客户端连续写,写完马上关闭连接,这时下面代码能打印出客户端的输出,//客户端关闭连接,下面的代码马上爆出异常,是这行代码。java.io.IOException: 您的主机中的软件中止了一个已建立的连接。//int nbytes = 0;//ByteBuffer echoBuffer = ByteBuffer.allocate(16);//while (true) {//echoBuffer.clear();//int r = sc.read(echoBuffer);//System.out.println(new String(echoBuffer.array()));//if (r <= 0) break;//echoBuffer.flip();//sc.write(echoBuffer);//nbytes += r;//}//System.out.println("echoed " + nbytes + " from " + sc);//下面的是处理过程是正确的。正确的做法就是对读取到n,0,-1分别处理,还要对客户端强制关闭的异常做处理
while (true) {
ByteBuffer buffer= ByteBuffer.allocate(2);
buffer.clear();intr;try{
r=sc.read(buffer);
System.out.println("r = " +r);
System.out.println(newString(buffer.array()));if (r < 0) {//客户端socket.close()会到这里,读取数r=-1
key.cancel();
System.out.println("cancel key for < 0");break;
}else if (r == 0) {//客户端socket没有关闭,而channel没有数据,数据数r=0。//有时候select()返回了,但channel不一定有数据。可能select()是被其他方法唤醒
break;
}
}catch(IOException e) {//客户端强制关闭会来这里报异常
e.printStackTrace();
key.cancel();
System.out.println("cancel key for Exception");break;
}
}//while
}//if ... else if//try {//Thread.sleep(500);//} catch (InterruptedException e) {//e.printStackTrace();//}
}//while
}//while
}private int doRead(SelectionKey key) throwsIOException {
SocketChannel channel=(SocketChannel) key.channel();while (true) {int count = -1;
ByteBuffer buffer= ByteBuffer.allocate(2);if (buffer.remaining() > 0) {
count=channel.read(buffer);
System.out.println("count = " +count);if (count <= 0) returncount;
}
}
}private static voidprintKeyInfo(SelectionKey sk) {
String s= newString();
s= "Att: " + (sk.attachment() == null ? "no" : "yes");
s+= ", Read: " +sk.isReadable();
s+= ", Acpt: " +sk.isAcceptable();
s+= ", Cnct: " +sk.isConnectable();
s+= ", Wrt: " +sk.isWritable();
s+= ", Valid: " +sk.isValid();
s+= ", interestOps: " +sk.interestOps();
s+= ", readyOps: " +sk.readyOps();
System.out.println(s);
}public static voidmain(String[] args) {try{newNIOServer2().startServer();
}catch(IOException e) {
e.printStackTrace();
}
}
}