Почему ключ должен быть удален в `selector.selectedKeys (). Iterator ()` в Java Java? - PullRequest
18 голосов
/ 20 августа 2011

Я нашел пример кода java nio:

 ServerSocketChannel server = ServerSocketChannel.open();  
 Selector selector = Selector.open();  
 server.socket().bind(new InetSocketAddress(8080));  
 server.configureBlocking(false); 
 server.register(selector, SelectionKey.OP_ACCEPT);  

 while(true) {
     selector.select(); 
     Iterator iter = selector.selectedKeys().iterator();  
     while (iter.hasNext()) {  
         SelectionKey key = (SelectionKey) iter.next();  
         iter.remove();  // Why remove it? 
         process(key);  
     }
 }

Когда он получает выбранные ключи, он удаляет ключ в цикле.Почему мы должны это делать?


ОБНОВЛЕНИЕ

Благодаря ответам, предоставленным EJP и user270349 ,Я думаю, что теперь я понимаю, позвольте мне объяснить это подробно.

В селекторе есть 2 таблицы:

  1. таблица регистрации: когда мы вызываем channel.register,в нем будет новый предмет (ключ).Только если мы позвоним key.cancel(), он будет удален из этой таблицы.

  2. готов к выбору таблицы: когда мы вызываем selector.select(), селектор ищет таблицу регистрации, находитключи, которые доступны, скопируйте ссылки на них в эту таблицу выбора.Элементы этой таблицы не будут очищены селектором (это означает, что даже если мы снова вызовем selector.select(), он не очистит существующие элементы)

Вот почему мы имеемвызвать iter.remove(), когда мы получили ключ из таблицы выбора.Если нет, мы получим ключ снова и снова selector.selectedKeys(), даже если он не готов к использованию.

Ответы [ 4 ]

12 голосов
/ 21 августа 2011

Поскольку селектор никогда этого не делает, он только добавляет к набору, поэтому, если вы этого не сделаете, вы сами обработаете событие в следующий раз, когда селектор вернется.

0 голосов
/ 13 апреля 2017

если вы не вызываете метод remove, вы обнаружите, что он также работает нормально。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@SuppressWarnings({ "unchecked" })
public class NioServer {
    ServerSocketChannel serverChannel;
    ServerSocket serverSocket;
    public final int port;
    private Selector selector;
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    NioServer(final int port) {
        this.port = port;
    }
    void init() throws Exception {
        // 创建 ServerSocketChannel、ServerSocket
        serverChannel = ServerSocketChannel.open();
        serverSocket = serverChannel.socket();
        serverSocket.bind(new InetSocketAddress(port));
        // 设置通道为非阻塞模式
        serverChannel.configureBlocking(false);
        // 开启通道选择器,并注册 ServerSocketChannel
        selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    void go() throws Exception {
        while (true) {
            int num = selector.select();
            if (num <= 0)
                continue;
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
            System.out.println(selector.selectedKeys().size()); //等于线程数量
            while (keyIter.hasNext()) {
                final SelectionKey key = keyIter.next();
                System.out.println("所有的key"+key);
                // 接收一个Socket连接
                // key.isAcceptable()如果为true,说明channnel支持accept(),也就是说明是一个ServerSocketChannel
                if (key.isAcceptable()) {
                    System.out.println("可以连接的key:"+key);
                    SocketChannel clientChannel = serverChannel.accept();
                    if (clientChannel != null) {
                        clientChannel.configureBlocking(false);
                        clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                    }
                }
                // 如果isReadable()为true,说明是一个SocketChannel
                if (key.isReadable()) {
                    String requestContent = read(key);
                    // 业务处理
                    // responseContent=doSomthing(requestContent);
                    write(key, "ok" /* responseContent */);
                }
//          keyIter.remove();
            }
        }
    }

    // 从通道读取数据
    String read(SelectionKey key) throws Exception {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        buffer.clear();// 这一步必须有
        int len = 0;
        StringBuffer str = new StringBuffer();
        while ((len = socketChannel.read(buffer)) > 0) {
            byte[] bs = buffer.array();
            String block = new String(bs, 0, len);
            System.out.println("Server read: " + block);
            str.append(block);

        }
        buffer.clear();
        return str.toString();
    }

    // 写数据到通道

    void write(SelectionKey key, String str) throws Exception {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        buffer.clear();
        buffer.put(str.getBytes());
        buffer.flip();// 这一步必须有
        socketChannel.write(buffer);

    }

    public static void main(String[] args) throws Exception {
        final int port = 10000;
        NioServer server = new NioServer(port);
        server.init();
        /// ========================================================
        // 接下来模拟3个Client并发访问服务器
        int poolsize = 1;
        ExecutorService pool = Executors.newFixedThreadPool(poolsize);
        Collection<Callable> tasks = new ArrayList<Callable>(10);
        final String clientname = "clientThread";
        for (int i = 0; i < poolsize; i++) {
            final int n = i;
            // 若每一个Client都保持使用BIO方式发送数据到Server,并读取数据。
            tasks.add(new Callable() {
                @Override
                public Object call() throws Exception {
                    Socket socket = new Socket("127.0.0.1", port);
                    final InputStream input = socket.getInputStream();
                    final OutputStream out = socket.getOutputStream();
                    final String clientname_n = clientname + "_" + n;
                    // BIO读取数据线程
                    new Thread(clientname_n + "_read") {
                        @Override
                        public void run() {
                            byte[] bs = new byte[1024];
                            while (true) {
                                try {

                                    Thread.sleep(1000);

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                int len = 0;
                                try {
                                    while ((len = input.read(bs)) != -1) {

                                        System.out.println("Clinet thread " + Thread.currentThread().getName()
                                                + " read: " + new String(bs, 0, len));
                                    }

                                } catch (IOException e) {

                                    e.printStackTrace();
                                }
                            }
                        }

                    }.start();
                    // BIO写数据线程
                    new Thread(clientname_n + "_write") {
                        @Override
                        public void run() {
                            int a = 0;
                            while (true) {
                                try {
                                    Thread.sleep(100);

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                                String str = Thread.currentThread().getName() + " hello, " + a;
                                try {
                                    out.write(str.getBytes());
                                    a++;

                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }

                    }.start();

                    return null;

                }

            });

        }
        pool.invokeAll((Collection<? extends Callable<Object>>) tasks);
        server.go();

    }

}

просто распечатайте его и проверьте его - вы обнаружите, что даже не удаляете , используйте key.isAcceptable () может фильтровать correntрезультат

0 голосов
/ 31 августа 2013

Поскольку удаление ключа из выбранного набора, как это было обработано, будет ожидать следующего события выбора.

выбранный набор содержит ключи готовых каналов.

 selector.select(); //This may block for a long time. Upon returning, the elected set contains keys of the ready channels.

Получить итератор для набора выбранных ключей и выполнить бизнес-информацию

Iterator it = selector.selectedKeys().iterator(  );

Окончательно удалить ключ из выбранного набора; это было обработано

 it.remove(  );

Ключи могут быть напрямую удалены из этого набора, но не добавлены. При попытке добавить к выбранному набору ключей выдается исключение java.lang.UnsupportedOperationException.

0 голосов
/ 20 августа 2011

Потому что, пока вы не сделаете это, вы не сможете обнаружить новые повторения событий.

...