Selector.select() keys several times?
I try to write network server using NIO and met strange problems. I have Thread selector select keys and than I create Action that execute each readable request in thread pool. But during debug I found out that in select loop for each readable request object Action was created several times!!!
Also I have problem with closing connections. During testing I kill my client app so connection to server killed without and close() method. Ofcourse it cause exceptions on server side, but added some try catch to make server stable to network problems and clients drops. But my try catch do not help fully. After several such killing of my client server stop accepting incomming connections
Here is my code of acceptions connections on server:
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import com.vimas.callcenter.server.data.*;
import java.nio.*;
import java.text.NumberFormat;
import java.text.DateFormat;
import java.text.*;
publicclass StatisticsServer{
privatestaticfinal Log log = Log.getInstance();
privatestaticfinalint DEFAULT_PORT = 7777;
privatestatic Properties prop =new Properties();
privatestatic Hashtable<Long, UserProfile> users =new Hashtable<Long, UserProfile> ();
private Thread_pool pool;
public StatisticsServer(){
}
publicstaticvoid main(String[] args){
StatisticsServer voiceChatServer =new StatisticsServer();
voiceChatServer.start();
}
publicvoid start(){
//read server properties:
int port, initial_pool_size, max_pool_size;
try{
prop.loadFromXML(new FileInputStream("config.xml"));
port = Integer.parseInt(prop.getProperty("port"));
initial_pool_size = Integer.parseInt(prop.getProperty("initial_pool_size"));
max_pool_size = Integer.parseInt(prop.getProperty("max_pool_size"));
Log.DEBUG = Boolean.valueOf(prop.getProperty("DEBUG")).booleanValue();
}
catch (Exception ex){
Log.DEBUG =true;
port = DEFAULT_PORT;
initial_pool_size = 100;
max_pool_size = 200;
log.info(
"Properties file (server.properties) or some keys not found, using default values: " + ex);
}
//create thread pool
pool =new Thread_pool(initial_pool_size, max_pool_size);
//create server socket
ServerSocketChannel serverChannel;
Selector selector;
try{
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address =new InetSocketAddress(port);
ss.bind(address);
log.info("Server started on port " + port);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
catch (IOException ex){
ex.printStackTrace();
log.error("Error starting server!", ex);
return;
}
//start while loop for receiving requests
try{
while (selector.select() > 0){
Iterator keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()){
//add try catch if some exception occurs it will not stop server work
try{
SelectionKey key = (SelectionKey) keyIterator.next();
keyIterator.remove();
if (key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.
channel();
SocketChannel client = server.accept();
log.debug("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey key2 = client.register(selector,
SelectionKey.OP_READ);
Long userID = genID();
UserProfile profile =new UserProfile(userID);
users.put(userID, profile);
key2.attach(userID);
}
elseif (key.isReadable()){
SocketChannel socketChannel = (SocketChannel) key.channel();
Long userID = (Long) key.attachment();
//craete Action for executing accepted command from client
Action a =new Action(userID, socketChannel, key);
//add action to thread pool for execution
pool.execute(a);
}
}
catch (Throwable ex2){
log.error("SelectionKey work fail error: " + ex2,ex2);
}
}
}
}
catch (IOException ex1){
ex1.printStackTrace();
}
}
publicstatic UserProfile getProfile(Long id){
return users.get(id);
}
publicstaticvoid removeProfile(Long id){
users.remove(id);
}
privatestatic Long genID(){
returnnew Long( (long) (Math.random() * 100000000));
}
}
I added system.out to Action constructor and find out that when one request is sent top sever, object Action creates several times.
When I kill client app, then I get exception in place where read from channel, and the most interesting thing that request was already executed and answer sent, so it must be exception are throw in second, 3-rd, etc. Action objects that were created for one request, but data were read by first Action object.
try{
recieveByteBuffer.clear();
if (!socketChannel.isConnected()){
return;
}
int nbytes = socketChannel.read(recieveByteBuffer);
if (nbytes == -1){
key.cancel();
socketChannel.close();
return;
}
if (nbytes > 0){
log.debug("read " + nbytes +" bytes");
recieveByteBuffer.flip();
parseBytes(recieveByteBuffer.array(), nbytes);
}
}
catch (Throwable ex){
try{
socketChannel.close();
}
catch (Exception ex2){
log.error("Action.run.catch error: " + ex2);
}
}
Exception:
java.io.IOException: An existing connection was forcibly closed by the remote host
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:25)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:233)
at sun.nio.ch.IOUtil.read(IOUtil.java:206)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:236)
at com.vimas.callcenter.server.Action.run(Action.java:54)
at com.vimas.callcenter.server.Thread_pool$Pooled_thread.run(Thread_pool.java:60)

