在一次服务器端接收客户端发送的消息时,服务器端一直触发读就绪事件,导致服务端代码出现死循环,代码如下
客户端
public class Client1 {
public static void main(String[] args) throws IOException, InterruptedException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
InetSocketAddress localhost = new InetSocketAddress("localhost", 8888);
socketChannel.connect(localhost);
while (!socketChannel.isConnected()) {
socketChannel.finishConnect();
}
socketChannel.write(ByteBuffer.wrap("客户端数据".getBytes()));
}
}
服务端
public class Server1 {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress(8888);
serverSocketChannel.bind(inetSocketAddress);
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, 16);
while (true) {
int select = selector.select();
System.out.println("当前有" + select + "个操作就绪");
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
if (next.isAcceptable()) {
System.out.println("accept就绪++++++++++++++");
ServerSocketChannel server = (ServerSocketChannel) next.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (next.isReadable()) {
System.out.println("读就绪+++++++++++++++++");
ByteBuffer allocate = ByteBuffer.allocate(10240000);
StringBuffer stringBuffer = new StringBuffer();
SocketChannel channel = (SocketChannel) next.channel();
int read = channel.read(allocate);
while (read > 0) {
allocate.flip();
stringBuffer.append(new String(allocate.array(), 0, read));
allocate.clear();
read = channel.read(allocate);
}
System.out.println(stringBuffer);
}
}
iterator.remove();
}
}
}
执行结果
最后在一个帖子里看到了答案https://segmentfault.com/q/1010000019694209
原因
- 如果客户端的socket断开连接后,会一直向服务端发送一个读就绪,即channel.read(allocate)=-1,这个时候服务端应该断开这个连接,或者至少注销掉OP_READ。
验证
- 在客户端的代码最后加一个线程睡眠,
Thread.sleep(1000000L);
可以看到,在客户端代码执行结束之前,服务端不会收到这个读就绪事件
解决
服务器端代码加上判断
public class Server1 {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress(8888);
serverSocketChannel.bind(inetSocketAddress);
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, 16);
while (true) {
int select = selector.select();
System.out.println("当前有" + select + "个操作就绪");
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
if (next.isAcceptable()) {
System.out.println("accept就绪++++++++++++++");
ServerSocketChannel server = (ServerSocketChannel) next.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (next.isReadable()) {
System.out.println("读就绪+++++++++++++++++");
ByteBuffer allocate = ByteBuffer.allocate(10240000);
StringBuffer stringBuffer = new StringBuffer();
SocketChannel channel = (SocketChannel) next.channel();
int read = channel.read(allocate);
while (read > 0) {
allocate.flip();
stringBuffer.append(new String(allocate.array(), 0, read));
allocate.clear();
read = channel.read(allocate);
}
System.out.println(stringBuffer);
// socket已经断开
if (read == -1) {
//增加注销操作
// int readyOps = next.readyOps();
//&~xx 代表取消事件,取反 按位与
// next.interestOps(next.interestOps() & ~readyOps);
// 或者断开服务端的这个socket
channel.close();
}
}
}
iterator.remove();
}
}
}