derThread.java#315 to #332). This works fine for one client socket, but does NOT work for a greater number of opened client sockets. I modified my test case and attach it to this mail. Simply expand in an arbitrary directory and compile with "javac *.java". Run the server with java ServerNIO -selectWait=xxx where xxx is the time passed to the Selector.select call. Try it with xxx=0 To start the client, use java Client -t=xxx where xxx is the number of threads that open a connection to the server, send 1024 bytes of data and close the socket. Please try it with 1, 10 and 100 client threads and watch the debug output of the server window. (Review ID: 134352) ====================================================================== Name: nt126004 Date: 11/27/2001 java version "1.4.0-beta3" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84) Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode) I'm using the nio package to write a "proxy" type application, so I have a ServerSocketChannel listening for incoming connections from a client and then create a SocketChannel to connect to a server. Both SocketChannels have a SelectionKey registered to the same Selector for OP_READ requests. All works fine, until either the client or server drops the connection. I would expect the SelectionKey to be triggered for one of the SocketChannels, but nothing appears to happen. I have tried using Selector.select(timeout) and then checking the status of each channel, but they still show isConnected() and isOpen() set to true. Shouldn't the OP_READ be triggered as the socket has been remotely shutdown ? Here's some sample code called TestProxy.java... I've added comments at the top of the file on how to use it... /** * Sample proxy program to demonstrate use of new classes in java.nio package. * * This will use a default listening port of 1414 and target port of 8900 and * assumes client, server and proxy are all running on the same machine. * * For the client and server, use the TimeQuery and NBTimeServer samples * shipped with Java 1.4. * * Start TestProxy in a command prompt (it listens on port 1414 by default). * * Start NBTimeServer in a command prompt (it listens on port 8900 by default). * * Start TimeQuery in a command prompt with the following options : * * java TimeQuery 1414 host_name * * (host_name must be the fully qualified name for TimeQuery to work). * * You can run TimeQuery as many times as you like. Each invocation will * cause a new Thread to be started to handle the request. The Selector * in TestProxy will timeout after 10 seconds and the status of SelectorKeys * SocketChannels and Sockets will be displayed, including the local port * number (this is used to identify each thread). * * Even though the client has dropped the connection, the status shows it's * still active. In fact, if you stop NBTimeServer, the connections still * appear to be connected. * */ import java.io.*; import java.nio.*; import java.nio.channels.*; import java.nio.channels.spi.*; import java.net.*; import java.util.*; // Listen on a port for connections and write back the current time. public class TestProxy { private static final int LISTENING_PORT = 1414; private static final int TARGET_PORT = 8900; private static Selector acceptSelector = null; // Constructor with no arguments creates a time server on default port. public TestProxy() throws Exception { acceptConnections(this.LISTENING_PORT); } // Constructor with port argument creates a time server on specified port. public TestProxy(int port) throws Exception { acceptConnections(port); } // Accept connections for current time. Lazy Exception thrown. private void acceptConnections(int port) throws Exception { // Selector for incoming time requests acceptSelector = SelectorProvider.provider().openSelector(); // Create a new server socket and set to non blocking mode ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); // Bind the server socket to the local host and port InetAddress lh = InetAddress.getLocalHost(); InetSocketAddress isa = new InetSocketAddress(lh, port); ssc.socket().bind(isa); System.out.println("acceptConnections() listening on port " + LISTENING_PORT); // Register accepts on the server socket with the selector. This // step tells the selector that the socket wants to be put on the // ready list when accept operations occur, so allowing multiplexed // non-blocking I/O to take place. SelectionKey acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT); int keysAdded = 0; // Here's where everything happens. The select method will // return when any operations registered above have occurred, the // thread has been interrupted, etc. while ((keysAdded = acceptSelector.select()) > 0) { System.out.println("acceptConnections() get list of keys"); // Someone is ready for I/O, get the ready keys Set readyKeys = acceptSelector.selectedKeys(); Iterator i = readyKeys.iterator(); // Walk through the ready keys collection and process date requests. while (i.hasNext()) { System.out.println("acceptConnections() get next key"); SelectionKey sk = (SelectionKey)i.next(); i.remove(); // The key indexes into the selector so you // can retrieve the socket that's ready for I/O // Assumed to be conenct request ServerSocketChannel nextReady = (ServerSocketChannel) sk.channel(); // Accept the date request and send back the date string Socket s = nextReady.accept().socket(); int p = s.getPort(); // New connection accepted... pass to own thread to handle System.out.println("acceptConnections() calling proxy"); SocketHandler sh = new SocketHandler(s, p); new Thread(sh).start(); System.out.println("acceptConnections() wait for next connection"); } } } // Entry point. public static void main(String[] args) { try { TestProxy tp = new TestProxy(); } catch (Exception e) { e.printStackTrace(); } } // This class acts a proxy. It gets given a socket, creates a connection // to a predefined target and then waits on OP_READ requests from either // side of the connection. Whatever it reads from one side, it writes to // the other side of the connection. public class SocketHandler implements Runnable { Socket socket; String port; SocketChannel channel; Selector ioSelector = null; // Constructor public SocketHandler(Socket soc, int p) { this.socket = soc; this.port = "proxy(" + p + ") "; } // Call main proxy code.... public void run() { try { proxy(socket); } catch (Exception e) { e.printStackTrace(); } } // This is where the real work occurs. private void proxy(Socket sToCaller) throws Exception { System.out.println(port); ByteBuffer buff = ByteBuffer.allocateDirect(64000); SocketChannel scToCaller = sToCaller.getChannel(); ioSelector = SelectorProvider.provider().openSelector(); System.out.println(port + "create SocketChannel to Server"); SocketChannel scToResp = SocketChannel.open(); InetAddress lh = InetAddress.getLocalHost(); System.out.println(port + "connecting Socket to localhost" + TARGET_PORT); boolean b = scToResp.connect(new InetSocketAddress(lh, TARGET_PORT)); // Must be non-blocking mode scToResp.configureBlocking(false); scToCaller.configureBlocking(false); // Register both SocketChannels to the same Selector SelectionKey skForResp = scToResp.register(ioSelector, SelectionKey.OP_READ + SelectionKey.OP_WRITE); SelectionKey skForCaller = scToCaller.register(ioSelector, SelectionKey.OP_READ + SelectionKey.OP_WRITE); int keysAdded = 0; try { // Stay here forever... we should drop out when either the client or server // drops the connection. while (true) { // Here's where everything happens. The select method will // return when any operations registered above have occurred, the // thread has been interrupted, etc. while ((keysAdded = ioSelector.select(10000)) > 0) { System.out.println(port + "get list of keys"); // Someone is ready for I/O, get the ready keys Set readyKeys = ioSelector.selectedKeys(); Iterator i = readyKeys.iterator(); // Walk through the ready keys collection and process requests. while (i.hasNext()) { System.out.println(port + "get next key"); SelectionKey sk = (SelectionKey)i.next(); showSelectionKey(sk); i.remove(); // The key indexes into the selector so you // can retrieve the socket that's ready for I/O SocketChannel nextReady = (SocketChannel)sk.channel(); // Clear the buffer buff.clear(); // Ready for reading if (sk.isReadable()) { System.out.println(port + "Reading..."); nextReady.read(buff); // Length of data read int pos = buff.position(); System.out.println(port + "Data length = " + pos); buff.flip(); // Display data read System.out.print(port); try { for (int j = 0; j < pos; j++ ) { String ss = Integer.toHexString(buff.get()); if (ss.length() == 1) System.out.print("0" + ss + " "); else if (ss.length() > 2) System.out.print(ss.substring(6) + " "); else System.out.print(ss + " "); } } catch (Exception e) { System.out.println(port + "Exception " + e.getMessage()); } System.out.println(port + " "); buff.flip(); // Write data to other side of connection if (sk.equals(skForCaller)) { System.out.println(port + "Writing to server ..."); scToResp.write(buff); } else { System.out.println(port + "Writing to client ..."); scToCaller.write(buff); } } else // Ready for writing... NOP if (sk.isWritable()) { System.out.println(port + "Writable..."); } else { // Just in case... System.out.println(port + "Uknown i/o"); } } } System.out.println(port + "dropped out of select()"); showKeys(); } } catch (ClosedSelectorException cse) { System.out.println(port + "Closed Selector exception..."); } catch (IOException ioe) { System.out.println(port + "IOException... " + ioe.getMessage()); } catch (Exception e) { System.out.println(port + "Exception... " + e.getMessage()); } } // Display status of all SelectionKeys private void showKeys() { Set readyKeys = ioSelector.keys(); Iterator i = readyKeys.iterator(); // Walk through the ready keys collection and process date requests. while (i.hasNext()) { SelectionKey sk = (SelectionKey)i.next(); showSelectionKey(sk); } } // Display status of SelectionKey private void showSelectionKey(SelectionKey sk) { System.out.println(port + "====SelectionKey======="); System.out.println(port + "interestOps : " + sk.interestOps()); System.out.println(port + "readyOps : " + sk.readyOps()); System.out.println(port + "isValid : " + sk.isValid()); System.out.println(port + "isConnectable : " + sk.isConnectable()); System.out.println(port + "isReadable : " + sk.isReadable()); System.out.println(port + "isWritable : " + sk.isWritable()); SocketChannel sc = (SocketChannel) sk.channel(); showSocketChannel(sc); } // Display status of SocketChannel private void showSocketChannel(SocketChannel ch) { System.out.println(port + "=======Channel======="); System.out.println(port + "isConnected : " + ch.isConnected()); System.out.println(port + "isInputOpen : " + ch.isInputOpen()); System.out.println(port + "isOutputOpen : " + ch.isOutputOpen()); System.out.println(port + "isOpen : " + ch.isOpen()); Socket s = (Socket) ch.socket(); showSocket(s); } // Display status of Socket private void showSocket(Socket s) { System.out.println(port + "=======Socket======="); System.out.println(port + "isBound : " + s.isBound()); System.out.println(port + "isClosed : " + s.isClosed()); System.out.println(port + "isConnected : " + s.isConnected()); System.out.println(port + "isInputShutdown : " + s.isInputShutdown()); System.out.println(port + "isOutputShutdown : " + s.isOutputShutdown()); } } } (Review ID: 134656) ====================================================================== Name: nt126004 Date: 11/27/2001 java version "1.4.0-beta3" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84) Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode) The problem is the behavior of the Selector if a Client closes its socket. I found out that in this case Selector.select() returns and the SelectionKey signals "isReadable". If I try to read data from the associated SocketChannel, I get the value -1 from the read operation. In this case, I close the SocketChannel (see Rea
|