Java Selector возвращает SelectionKey с OP_READ без данных в бесконечном цикле после записи в канал - PullRequest
4 голосов
/ 29 октября 2011

У меня проблемы с моим кодом: я написал простой клиент SocketChannel с Selector, после запуска он успешно читает сообщения с сервера (сервер отправляет события).Но после записи в сокет (см. Основной метод) селектор начинает возвращать читаемый сокет в цикле infinyty, handleKey возвращает прочитанные -1 байт, поэтому селектор все время возвращает 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 ]

6 голосов
/ 30 октября 2011

read() возвращает -1 в EOS, который вы полностью игнорируете.Когда вы получаете EOS, вы должны либо закрыть канал, либо хотя бы отменить интерес к OP_READ.В противном случае вы просто получите еще один OP_READ и еще один -1, когда будете читать, как вы делаете, навсегда.Вопреки вашим комментариям выше, read() возвращает ноль при пустом чтении.Вы можете игнорировать это, на самом деле вы даже не увидите его, если будете читать только когда isReadable(), если только вы не читаете в цикле, но вы не должны игнорировать EOS.

0 голосов
/ 29 октября 2011

read () возвращает -1, когда он прочитал EOF. Определение:

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

Это означает, что вы должны отменить регистрацию процентов для OP_READ.

...