Question

I use SocketChannel to receive TCP stream from server on client side. For example:

Selector selector=Selector.open();
SocketChannel mychannel=SocketChannel.open(new InetSocketAddress(ip,port));
channel.configureBlocking(false);
SelectionKey channelkey=channel.register(selector,SelectionKey.OP_READ);

Then, I can use the selector.select() method to handle reading problem.

while(true){
    if(selector.select()!=0){
       Iterator<SelectionKey> it=selector.selectedKeys().iterator();
       while(it.hasNext()){
         SelectionKey key=it.next();
         it.remove();
         if(key.isReadable()){
            if(key.equals(channelkey)){

               //concrete handle
               ...
            }
         }
       }
    }
}

With concrete handle,considering I'd like to use InputStream(I wanna read stream line) to receive tcp stream from server side,there are two methods.One is using channel.socket(), another is to use Channels. Here I use channel.socket(), for example:

SocketChannel channel = (SocketChannel) key.channel();
key.cancel();
channel.configureBlocking(true);
InputStream ins = Channels.newInputStream(channel);
InputStreamReader is = new InputStreamReader(ins,"utf-8");
BufferedReader in = new BufferedReader(is);
String res = in.readLine();
while (!res.equals("")) {
    System.out.println(res);
    res = in.readLine();
}
       ......①

OK, now I finish reading tcp stream for one time.In order to continue to using selector, I should set channel blocking mode to false.

    channel.configureBlocking(false);

The question is,the key that combines channel and selector has been canceled.I want to register mychannel again.What should I do? It seems that if I use mychannel.register(selector, SelectionKey.OP_READ) again on ①, it throws Exception.

My run() method code is as follows:

try {
if (selector.select(getTimeout()) != 0) {
    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
    while (it.hasNext()) {
        SelectionKey key = it.next();
        if (key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            key.cancel();
            channel.configureBlocking(true);
            InputStream ins = Channels.newInputStream(channel);
            InputStreamReader is = new InputStreamReader(ins,"utf-8");
            BufferedReader in = new BufferedReader(is);
            String res = in.readLine();
            while (!res.equals("")) {
                System.out.println("========" + res);
                res = in.readLine();
            }
            channel.configureBlocking(false);
            System.out.println(key.isValid());
            proxykey=channel.register(selector, SelectionKey.OP_READ);
        }
        it.remove();
    }
}
} catch (IOException ex) {
    ex.printStackTrace();
}

The Exception it throws is:

    Exception in thread "Thread-0" java.nio.channels.CancelledKeyException
at sun.nio.ch.SelectionKeyImpl.ensureValid(Unknown Source)
at sun.nio.ch.SelectionKeyImpl.interestOps(Unknown Source)
at java.nio.channels.spi.AbstractSelectableChannel.register(Unknown Source)
at java.nio.channels.SelectableChannel.register(Unknown Source)
at AgentThread.run(AgentThread.java:185)
Was it helpful?

Solution

SelectionKey.cancel() doesn't take full effect until the next select(), for various arcane reasons. You could try calling selectNow() after the cancel, or maybe better just before the re-registration.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top