Java NIO selector.select()始终只为OP_ACCEPT SelectionKey返回一个SelectionKey

时间:2014-04-17 06:16:03

标签: java sockets nio

我编写了一个使用选择器的java NIO服务器来接受来自客户端的连接。

我以为selector.select()会阻塞并返回多个键。我刚刚为OP_ACCEPT注册了选择器,它总是一次返回1个键

我做错了吗?

package com.bay.srikanth.client;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class FloodServer {

    private final static Logger LOGGER = Logger.getLogger(FloodServer.class.getName());

    private static int PORT = 5555;
    private static ConcurrentHashMap<Integer, SocketChannel> chm
                        = new ConcurrentHashMap<Integer, SocketChannel>();
    private static int msg = 0;
    public static void main(String args[]) throws Exception {
        // Create a new selector
        Selector selector = Selector.open();
        // Open a listener on each port, and register each one
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ServerSocket ss = ssc.socket();            
        InetSocketAddress address = new InetSocketAddress(PORT);
        ss.bind(address);
        //registers ACCEPT
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Going to listen on " + PORT);

        while (true) {
            LOGGER.log(Level.INFO, "Total active connections : " + chm.size());
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            LOGGER.log(Level.INFO, "Selected Keys on : " + selectedKeys + " with keys count : " + selectedKeys.size());
            Iterator<SelectionKey> it = selectedKeys.iterator();            

            while (it.hasNext()) {
                SelectionKey key = (SelectionKey) it.next();
                if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
                    // Accept the new connection
                    ServerSocketChannel sscNew = (ServerSocketChannel) key.channel();
                    SocketChannel sc = sscNew.accept();
                    sc.configureBlocking(false);
                    // Add the new connection to the selector                    
                    //sc.register(selector, SelectionKey.OP_READ);
                    // Add the socket channel to the list
                    chm.put(sc.hashCode(), sc);
                    it.remove();
                }
            }
        }        
    }        
}

Flood Test客户端工具

package com.bay.srikanth.client;

import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.Vector;

public class FloodTest implements Runnable {

private static List<SocketChannel> channelSet = new Vector<SocketChannel>();

@Override
public void run() {
    try {
        for(int i=0; i<50; i++) {
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(true);
            //socketChannel.connect(new InetSocketAddress("10.9.242.70", 5555));
            socketChannel.connect(new InetSocketAddress("localhost", 5555));
            channelSet.add(socketChannel);
        } 
    } catch(Exception ex) {
        System.out.println(ex);
    }
    System.out.println(channelSet.size());
    try {
        Thread.sleep(30000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void main(String args[]) throws InterruptedException {
    for(int i=0; i<100; i++) {
        new Thread(new FloodTest()).start();
    }
}

}

服务器上的响应日志:(部分)

Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Selected Keys on : [sun.nio.ch.SelectionKeyImpl@5e176f] with keys count : 1
Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Total active connections : 2328
Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Selected Keys on : [sun.nio.ch.SelectionKeyImpl@5e176f] with keys count : 1
Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Total active connections : 2329
Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Selected Keys on : [sun.nio.ch.SelectionKeyImpl@5e176f] with keys count : 1
Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Total active connections : 2330
Apr 17, 2014 11:35:49 AM com.bay.srikanth.client.FloodServer main
INFO: Selected Keys on : [sun.nio.ch.SelectionKeyImpl@5e176f] with keys count : 1

始终,选择键上:[sun.nio.ch.SelectionKeyImpl@5e176f],键数:1,键数始终为1.我尝试增加线程,仍然是1。

有人可以回答这里的错误吗?

谢谢, SRIKANTH

3 个答案:

答案 0 :(得分:1)

服务器套接字一次只能接受一个。

当你选择了多个套接字时,如果你接受一个客户端,如果你为OP_READ事件注册该套接字,那么当接受和读取事件发生在两个套接字上时,你可能会得到两个密钥。

只有在执行语句之前发生事件时,才会选择两个或更多键 selector.select();

当你在select中阻塞时,它将在单个事件之后出现,它不会阻止多个事件发生。 select()阻塞,直到至少有一个通道为您注册的事件做好准备。

在您的情况下,您只注册了一个服务器套接字。所以你只能获得一把钥匙。 我想这回答了你的问题。

http://tutorials.jenkov.com/java-nio/selectors.html

答案 1 :(得分:0)

//sc.register(selector, SelectionKey.OP_READ);

这是您需要的线路。它被注释掉了。为什么呢?

注意,当你得到它时,你还需要一些代码来处理OP_READ,你需要创建它,remove()无条件和故障安全。最好的方法是在调用it.next()后直接移动它。

答案 2 :(得分:0)

我认为主要关键是你的客户只是一个线程。 虽然你有代码:

for(int i=0; i<50; i++) {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(true);
        //socketChannel.connect(new InetSocketAddress("10.9.242.70", 5555));
        socketChannel.connect(new InetSocketAddress("localhost", 5555));
        channelSet.add(socketChannel);
    }

50的请求逐个发送到服务器,因此您的服务器将逐个接受。

总结:服务器套接字一次只能接受一个