FULL PRODUCT VERSION :
java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Server VM (build 1.6.0_03-b05, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
1. Linux hostname 2.4.21-27.ELsmp #1 SMP Wed Dec 1 21:59:02 EST 2004 i686 i686 i386 GNU/Linux
2. HP-UX hostname2 B.11.23 U ia64 2718609364 unlimited-user license
A DESCRIPTION OF THE PROBLEM :
The NIO selector wakes up infinitely in this situation..
0. server waits for connection
1. client connects and write message
2. server accepts and register OP_READ
3. server reads message and remove OP_READ from interest op set
4. client close the connection
5. server write message (without any reading.. surely OP_READ is not set)
6. server's select wakes up infinitely with return value 0
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
First run the TestServer.
java -cp . TestServer 7777
In another console, run the TestClient
java -cp . TestClient 192.168.1.34 7777
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expected nothing will happen (the selector should not woke up).
ACTUAL -
In windows, it worked as I expected but, in Linux and HP/UX the selector goes into the infinite loop.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
TestServer.java
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
import java.util.logging.*;
public class TestServer {
private static final long SLEEP_PERIOD = 5000L; // 5 seconds
private static final int BUFFER_SIZE = 8192;
private int port;
public TestServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Throwable {
if (args.length < 1) {
System.err.println("Usage : java TestServer <port>");
System.exit(0);
}
new TestServer(Integer.parseInt(args[0])).start();
}
public void start() throws Throwable {
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
ServerSocket server = serverChannel.socket();
server.bind(new InetSocketAddress(port));
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
SocketChannel clientChannel = null;
System.out.println("0. SERVER STARTED TO LISTEN");
boolean writeNow = false;
while (true) {
try {
// wait for selection
int numKeys = selector.select();
if (numKeys == 0) {
System.err.println("select wakes up with zero!!!");
}
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey selected = (SelectionKey) it.next();
int ops = selected.interestOps();
try {
// process new connection
if ((ops & SelectionKey.OP_ACCEPT) != 0) {
clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
// register channel to selector
clientChannel.register(selector, SelectionKey.OP_READ, null);
System.out.println("2. SERVER ACCEPTED AND REGISTER READ OP : client - " + clientChannel.socket().getInetAddress());
}
if ((ops & SelectionKey.OP_READ) != 0) {
// read client message
System.out.println("3. SERVER READ DATA FROM client - " + clientChannel.socket().getInetAddress());
readClient((SocketChannel) selected.channel(), buffer);
// deregister OP_READ
System.out.println("PREV SET : " + selected.interestOps());
selected.interestOps(selected.interestOps() & ~SelectionKey.OP_READ);
System.out.println("NEW SET : " + selected.interestOps());
Thread.sleep(SLEEP_PERIOD * 2);
new WriterThread(clientChannel).start();
}
} finally {
// remove from selected key set
it.remove();
}
}
} catch (IOException e) {
System.err.println("IO Error : " + e.getMessage());
}
}
}
public void readClient(SocketChannel channel, ByteBuffer buffer) throws IOException {
try {
buffer.clear();
int nRead = channel.read(buffer);
if (nRead < 0) {
channel.close();
return;
}
if (buffer.position() != 0) {
int size = buffer.position();
buffer.flip();
byte[] bytes = new byte[size];
buffer.get(bytes);
System.out.println("RECVED : " + new String(bytes));
}
} catch (IOException e) {
System.err.println("IO Error : " + e.getMessage());
channel.close();
}
}
static class WriterThread extends Thread {
private SocketChannel clientChannel;
public WriterThread(SocketChannel clientChannel) {
this.clientChannel = clientChannel;
}
public void run() {
try {
writeClient(clientChannel);
System.out.println("5. SERVER WRITE DATA TO client - " + clientChannel.socket().getInetAddress());
} catch (IOException e) {
System.err.println("5. SERVER WRITE DATA FAILED : " + e);
}
}
public void writeClient(SocketChannel channel) throws IOException {
try {
ByteBuffer buffer = ByteBuffer.wrap("zwxydfdssdfsd".getBytes());
int total = buffer.limit();
int totalWrote = 0;
int nWrote = 0;
while ((nWrote = channel.write(buffer)) >= 0) {
totalWrote += nWrote;
if (totalWrote == total) {
break;
}
}
} catch (IOException e) {
System.err.println("IO Error : " + e.getMessage());
channel.close();
}
}
}
}
TestClient.java
import java.util.logging.*;
import java.io.*;
import java.net.*;
public class TestClient {
private static final long SLEEP_PERIOD = 5000L; // 5 seconds
private String host;
private int port;
public TestClient(String host, int port) {
this.host = host;
this.port = port;
}
public static void main(String[] args) throws Throwable {
if (args.length < 2 || args[0].equals("127.0.0.1") || args[0].equals("localhost")) {
System.err.println("Usage : java TestClient <host name> <port> (host name should not be localhost)");
System.exit(0);
}
new TestClient(args[0], Integer.parseInt(args[1])).start();
}
public void start() throws Throwable {
Socket socket = new Socket(host, port);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream()),
true /* auto flush */);
out.println("abcdef");
System.out.println("1. CLIENT CONNECTED AND WROTE MESSAGE");
Thread.sleep(SLEEP_PERIOD);
// socket.shutdownOutput();
socket.close();
System.out.println("4. CLIENT SHUTDOWN OUTPUT");
Thread.sleep(SLEEP_PERIOD * 3);
}
}
---------- END SOURCE ----------