java nio ServerSocketChannel accept是如何工作的?

java nio ServerSocketChannel accept是如何工作的?,java,sockets,network-programming,nio,nonblocking,Java,Sockets,Network Programming,Nio,Nonblocking,我搞不懂NIO在幕后是怎么工作的。下面是一个示例代码: // Create the server socket channel ServerSocketChannel server = ServerSocketChannel.open(); // nonblocking I/O server.configureBlocking(false); // host-port 8000 server.socket().bind(new java.net.InetSocketAddress(host,80

我搞不懂NIO在幕后是怎么工作的。下面是一个示例代码:

// Create the server socket channel
ServerSocketChannel server = ServerSocketChannel.open();
// nonblocking I/O
server.configureBlocking(false);
// host-port 8000
server.socket().bind(new java.net.InetSocketAddress(host,8000));

// Create the selector
Selector selector = Selector.open();
// Recording server to selector (type OP_ACCEPT)
server.register(selector,SelectionKey.OP_ACCEPT);

while (true) {
      selector.select(); // blocking operation
      Iterator it = selector.selectedKeys().iterator();
      while (it.hasNext()) {
        SelectionKey selKey = (SelectionKey) it.next();

        // THE MOST INTRIGUING PART HERE!!!
        if (selKey.isAcceptable()) {
          ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel();
          SocketChannel sc = ssChannel.accept();
        }
        it.remove();
     }
}
这里我有几个问题:

  • selKey.channel()返回一个ServerSocketChannel是否与我们在Begging中使用ServerSocketChannel.open()创建的频道完全相同?如果不是,那是什么
  • 更重要的问题:在大多数其他教程中,selKey.channel();步骤被跳过,它们只使用SocketChannel client=server.accept();例如here:和here:那么,server.accept()如何知道我们处理的当前密钥
  • 他们甚至建议接受新线程中的频道。我想可能会出现以下情况

    key1 someClient1 acceptable
    key2 someClient2 not acceptable
    key3 someClient3 acceptable
    
    startThread1
    startThread3
    
    scheduler decides to give time to thread3 instead of thread1
    
    thread3 -> socket.accept() <- actually accepts client1
    thread1 -> socket.accept() <- actually accepts client3
    
    key1 someClient1可接受
    key2 someClient2不可接受
    键3某些客户3可接受
    开始线程1
    开始线程3
    调度程序决定将时间分配给thread3而不是thread1
    thread3->socket.accept()socket.accept()
    selKey.channel()返回一个ServerSocketChannel它与我们在开始时用ServerSocketChannel.open()创建的频道完全相同吗

    更重要的问题:在大多数其他教程中,selKey.channel();步骤被跳过,它们只使用SocketChannel client=server.accept();例如here:和here:那么,server.accept()如何知道我们处理的当前密钥

    没有。他们假设只有一个
    服务器socketchannel
    。你的方法更好:更一般

    他们甚至建议接受新线程中的频道

    我不知道为什么。这是一个非阻塞呼叫。它将立即返回。这个建议毫无意义。别理它。六年前的教程质量很差,但十三年前有更好的教程。试试Oracle教程。这篇文章的作者似乎根本不理解非阻塞模式的意义。对每一个事件使用单独线程的建议是完全荒谬的。他也不知道如何使用OP_WRITE。他对
    cancel()
    作了错误的断言。我可以继续。他是否执行过这段代码值得怀疑:他肯定没有以任何方式调查它的行为。如何编写不可扩展的NIO服务器。相当了不起

    我想可能会出现以下情况

    key1 someClient1 acceptable
    key2 someClient2 not acceptable
    key3 someClient3 acceptable
    
    startThread1
    startThread3
    
    scheduler decides to give time to thread3 instead of thread1
    
    thread3 -> socket.accept() <- actually accepts client1
    thread1 -> socket.accept() <- actually accepts client3
    
    我甚至不明白为什么要同时在两个线程中接受,更不用说它接受哪个客户端对哪个线程可能有什么影响了。这是一个在不存在的地方发明的难题

    所以,您能解释一下选择器是如何与ServerSocketChannel和accept方法成对工作的吗?因为我不明白“接受”以何种顺序接受客户,以及该顺序与所选键的关系

    accept()
    方法返回待办事项队列中的下一个套接字,并且每当待办事项队列为非空时触发OP_accept。这很简单,没有神秘感。该顺序根本不“与所选关键点相关”。所选键是
    服务器socketchannel
    的键

    编辑:看来你有一个重大的误解。考虑:

  • 您可以为
    OP\u ACCEPT
    创建并注册一个
    ServerSocketChannel
  • 两个客户端同时连接
  • 现在正好有一个
    SelectionKey
    ,因此在所选的密钥集中正好有一个:
    ServerSocketChannel
  • 然后在该键上处理
    isAcceptable()
    大小写;接受一个或两个连接;为
    OP\u READ注册这些频道。
  • 现在存在三个选择关键点,而在选定关键点集中没有选择关键点,因为您清除了它
  • 两个客户端都发送一些数据
  • 现在,您有两个选择键可以在“选择键集”中读取
  • 好吗

    我是否可以简单地执行以下操作:

    int availableClients = 0;
    while (it.hasNext()) {
        SelectionKey selKey = (SelectionKey) it.next();
    
        if (selKey.isAcceptable()) {
            ++availableClients;
        }
        it.remove();
    }
    for (int i = 0; i < availableClients; ++i) {
           SocketChannel sc = server.accept();
           doSomething(sc);
    }
    

    当然可以,但是为什么呢?把简单的东西变成复杂的东西是没有好处的。用第一种方法做。

    哇!谢谢,这几乎是我想听到的,但在我接受它作为回答之前,我想请您更详细地描述以下句子:“accept()方法返回backlog队列中的下一个套接字,并且每当backlog队列为非空时,OP_uaccept就会激发。”我最后一句关于“++availableClients”的语句只是因为我不明白acceptable key和accept()之间的相关性是什么。看起来我们可以处理一个密钥,但接受另一个客户端,这让我感到困惑。提前谢谢。正确。处理
    ServerSocketChannel
    键,并接受新创建的连接到新客户端的
    SocketChannel
    。相关关系是当OP_uAccept激发时,
    ACCEPT()
    不会返回
    null
    。我的意思是,如果client1有key1,client2有key2,selectedKeys返回[key1,key2],那么serverChannel.ACCEPT()可能会首先为client2创建新频道。然后,当我们处理key2时,它将为client1创建新的通道。所以,我认为顺序实际上是随机的(至少对于非阻塞的东西),我错了吗?什么键?什么客户?你两个都还没有。在您(i)接受连接,然后(ii)注册新的套接字通道之前,客户端没有任何密钥。客户机按其放入待办事项队列的顺序被接受,这取决于完成TCP握手的顺序。“客户机故障”的概念毫无意义。没有两种类型的钥匙,我也没有说过其他的。我说的“客户机密钥”指的是不存在的东西。有一种类型的键:
    SelectionKey。
    有两种类型的频道
    ServerSocketChannel
    SocketChannel.
    我认为这一切都不重要。你在什么地方有个严重的误会,但我无法确定。