写入通道后,Java Selector返回带有OP_READ的SelectionKey,而无需在无限循环中的数据

时间:2011-10-29 08:59:34

标签: java selector nio socketchannel

我的代码出了问题:我用Selector写了简单的SocketChannel客户端,启动后成功从服务器读取消息(服务器发送事件)。但是在写入socket(参见main方法)之后,选择器开始在infinyty循环中返回可读套接字,handleKey返回-1个字节readed,因此选择器所有时间都返回OP_READ SelectionKey而没有数据用于读取。 对不起我的英语不好。

感谢。

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;

}

2 个答案:

答案 0 :(得分:6)

read()在EOS返回-1,你完全忽略了。当您获得EOS时,您必须关闭频道或至少取消注册对OP_READ的兴趣。否则,当你正在做的时候,你将永远得到另一个OP_READ和另一个-1。与上面的评论相反,read()在空读时返回。您可以忽略这一点,如果您只在isReadable()时阅读,您甚至都不会看到它,除非您在循环中读取,但您不能忽略EOS。

答案 1 :(得分:0)

read()在读取EOF时返回-1。定义:

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

这意味着您应取消注册OP_READ的兴趣。