服务端代码:
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 TestDisconnectServer {
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();
}
}
}
}
客户端代码:
package NonBlocking;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class TestDisconnectClient {
static SocketChannel socketChannel = null;
static Selector selector = null;
public static void main(String[] args) throws IOException {
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
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.isReadable()) {
System.out.println("有数据可读");
}
iterator.remove();
}
}
}
}
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.Iterator;
public class TestDisconnectClient {
static SocketChannel socketChannel = null;
static Selector selector = null;
public static void main(String[] args) throws IOException {
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
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.isReadable()) {
System.out.println("有数据可读");
SocketChannel canReadChannel = (SocketChannel)sk.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
while (canReadChannel.read(buf) > 0) {
buf.flip();
System.out.println(new String(buf.array()));
buf.clear();
}
} catch (IOException e) {
canReadChannel.close();
sk.cancel();
System.out.println("检测到远程连接断开");
e.printStackTrace();
}
}
iterator.remove();
}
}
}
}
canReadChannel.read(buf)
抛出异常时,则是连接断开了。需要关闭channel,且取消SelectionKey。服务端加一句:
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("接受到新的客户端连接");
socketChannel.close(); //加一句。接受连接后,马上close
}
客户端修改:
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.Iterator;
public class TestDisconnectClient {
static SocketChannel socketChannel = null;
static Selector selector = null;
public static void main(String[] args) throws IOException {
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
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.isReadable()) {
System.out.println("有数据可读");
SocketChannel canReadChannel = (SocketChannel)sk.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
int readResult;
while ((readResult = canReadChannel.read(buf)) > 0) {
buf.flip();
System.out.println(new String(buf.array()));
buf.clear();
}
if (readResult == -1) {
//需要单独检测read返回-1的情况
System.out.println("readResult is -1");
canReadChannel.close();
sk.cancel();
}
} catch (IOException e) {
canReadChannel.close();
sk.cancel();
System.out.println("检测到远程连接断开");
e.printStackTrace();
}
}
iterator.remove();
}
}
}
}
如果一方调用shutdownOutput后,那么连接会处于半关闭状态,另一方也会不断收到READ事件,但表现是canReadChannel.read(buf)
的返回值为-1.注意也是不抛出异常。
修改服务端代码:
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class TestDisconnectServer {
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("接受到新的客户端连接");
socketChannel.shutdownOutput();
} else if (sk.isReadable()) {
System.out.println("有数据可读");
SocketChannel canReadChannel = (SocketChannel)sk.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
int readResult;
while ((readResult = canReadChannel.read(buf)) > 0) {
buf.flip();
System.out.println(new String(buf.array()));
buf.clear();
}
if (readResult == -1) {
//需要单独检测read返回-1的情况
System.out.println("readResult is -1");
canReadChannel.close();
sk.cancel();
}
} catch (IOException e) {
canReadChannel.close();
sk.cancel();
System.out.println("检测到远程连接断开");
e.printStackTrace();
}
}
iterator.remove();
}
}
}
}
修改客户端代码:
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.Iterator;
public class TestDisconnectClient {
static SocketChannel socketChannel = null;
static Selector selector = null;
public static void main(String[] args) throws IOException {
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
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.isReadable()) {
System.out.println("有数据可读");
SocketChannel canReadChannel = (SocketChannel)sk.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
int readResult;
while ((readResult = canReadChannel.read(buf)) > 0) {
buf.flip();
System.out.println(new String(buf.array()));
buf.clear();
}
if (readResult == -1) {
//需要单独检测read返回-1的情况
System.out.println("readResult is -1");
canReadChannel.close();
sk.cancel();
}
} catch (IOException e) {
canReadChannel.close();
sk.cancel();
System.out.println("检测到远程连接断开");
e.printStackTrace();
}
}
iterator.remove();
}
}
}
}
先后运行服务端、客户端。
客户端效果:
selector 1th loop, ready event number is 1
有数据可读
readResult is -1
//然后程序阻塞在下一次循环的select那里
服务端效果:
selector 1th loop, ready event number is 1
接受到新的客户端连接
selector 2th loop, ready event number is 1
有数据可读
readResult is -1
//然后程序阻塞在下一次循环的select那里
整个流程是这样的:
canReadChannel.close()
,这又使得服务端检测到了READ事件,且read结果也为-1。