4

I've trouble with my code: i've written simple SocketChannel client with Selector, after starting it successfully reads messages from server (server sends events). But after writing to socket (see main method) selector starts returning readable socket in infinyty loop, handleKey returns that -1 bytes readed, so selector all time returns OP_READ SelectionKey without data for reading. Sorry for my English.

Thanks.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class SelectorTest
{

    public SelectorTest() throws IOException {
        selector = Selector.open();
    }

    private void runSelector() {

        new Thread(new Runnable() {

            public void run()
            {

                alive = true;
                try {
                    while(alive) {
                        System.out.println("Selector started...");

                        selector.select();

                        Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

                        while(keyIter.hasNext()) {

                            SelectionKey key = keyIter.next();

                            keyIter.remove();

                            handleKey(key);
                        }
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }).start();
    }

    private void handleKey(SelectionKey key) throws IOException {

        SocketChannel chan = (SocketChannel) key.channel();
        System.out.println("Processing selected...");

        if(key.isConnectable()) {
            System.out.println("Connecting ...");
            if(chan.finishConnect()) {
                key.interestOps(SelectionKey.OP_READ);
            } else {
                key.channel();
            }
        } else if(key.isReadable()) {
            System.out.println("Processing reading...");

            ByteBuffer buf = ByteBuffer.allocate(1024);
            int readedBytes = chan.read(buf);
            System.out.println("Readed: " + readedBytes);
            buf.flip();

            for(byte b : buf.array()) {
                System.out.print((char) b);
            }
        } else if(key.isWritable()) {
            System.out.println("Finishing writing...");

            key.interestOps(SelectionKey.OP_READ);
        }
    }

    public static void main(String[] args) throws IOException {

        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(new InetSocketAddress("t1.sis.lan", 6001));

        SelectorTest ds = new SelectorTest();
        ds.runSelector();

        channel.register(ds.selector, SelectionKey.OP_CONNECT);

        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

        for(;;) {

            String line = in.readLine();
            if(line==null) break;

            if(line.toLowerCase().equals("bye")) break;

            if (line.toLowerCase().equals("write")) {
                String command = "GET_STREAMS\r\n\0";

                ByteBuffer buf = ByteBuffer.allocate(1024);
                buf.put(command.getBytes());
                buf.flip();

                channel.write(buf);
            }

            System.out.println("echo: "+line); // is it alive check
        }

        ds.alive = false;
        ds.selector.wakeup();
        channel.close();
    }

    private Selector selector;
    private boolean alive;

}
user207421
  • 305,947
  • 44
  • 307
  • 483
befire
  • 140
  • 1
  • 6

2 Answers2

6

read() returns -1 at EOS, which you are completely ignoring. When you get EOS, you must either close the channel or at least deregister interest in OP_READ. Otherwise you will just get another OP_READ and another -1 when you read, as you are doing, forever. Contrary to your comments above, read() returns zero on an empty read. You can ignore that, indeed you won't even see it if you only read when isReadable(), unless you read in a loop, but you must not ignore EOS.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thanx for answer. You mean that when read() returns -1 - server shutdowned socket on it's side? – befire Oct 30 '11 at 12:24
  • It was a problem on server side (it written not by me with C++), protocol describes that every command must be ended with \0, but when it receives \0 send EOS back. And it's successfully works without \0 :). I've just forgot add \0 in netty implementation of client to end of packet and it works fine :) . And just checked with raw SocketChannel and Selectors, it works too. So thanx everybody for help. – befire Oct 30 '11 at 13:08
  • @befire read() returns -1 at EOS. EOS means the peer either closed his socket or shut it down for output. You were ignoring that condition and getting into an ES loop. The server side problem you now describe is additional. – user207421 Oct 30 '11 at 22:06
1

read() returns -1 when it has read EOF. Definition:

read() returns: The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream

This means you should unregister the interest for OP_READ.

Lycha
  • 9,937
  • 3
  • 38
  • 43
  • Tnx for answer, but i'll need read events from server all time, while program running, not only response for my command. And yes, read() returns -1 during empty reads. Output to console looks like this: Key count: 1 Processing selected... Processing reading... Readed: 6 END // its successfull read after it's go empty Readed returns -1 – befire Oct 29 '11 at 10:31
  • 1
    @befire Are you saying you want to continue reading after you get EOF? That is not possible since nothing can be read from socket after EOF. And read() should not return -1 during empty reads, only when EOF. – Lycha Oct 29 '11 at 11:00
  • Ok, I'll describe my simple porgram for fully understanding: client connects to server and starts listen events (it's async, so at any time it may come), if need, client gets list of current sessions on server (write command to server and reads response like an event). In events client receives changes in this sessions (add, remove, change state etc.), so because it I need read from socket all time, when data exists in socket (event or response came). Tnx for help again. – befire Oct 29 '11 at 11:29
  • And, as I said selector returns SelectionKey for reading without any data in SocketChannel and read() returns -1. That is a problem, becasue select() not block and load CPU. This happens only when I write() to channel, before it's working as needed. – befire Oct 29 '11 at 11:33
  • There is no need to call shutdownInput(): this adds nothing when a FIN has already been received. He must either close the channel or at least deregister OP_READ. – user207421 Oct 30 '11 at 02:36