Java nio服务器总是停留在selector.select上

我正在尝试使用NIO编写简单的客户机服务器模型(以了解它如何工作)。 有两种类型的消息可以发送Connection(这是服务器发送给客户端的消息,当它连接给它的信息时,如客户端ID)和Ping。

如果客户端没有被ping超过4秒的时间段,服务器将ping客户端,客户端将响应乒乓。

当我初始化我的服务器时,我这样做:

    try{
        selector = Selector.open();
        selector.wakeup();
        serverChannel = selector.provider().openServerSocketChannel();
        serverChannel.socket().bind(new InetSocketAddress(port));
        serverChannel.configureBlocking(false);

        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }catch (IOException e){
        log.Error(e);
        throw new RuntimeException("Error opening selector.", e);
    }

在发生这种情况之后,我们进入我们的服务器循环,它将查找哪些键已被选择并在键上执行所选操作

int select = 0;

    while (running){ 
        try{
           select = selector.select(timeout);

           if(select == 0){
               continue;
           }
           Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();

           while(selectedKeys.hasNext()){
               SelectionKey key = selectedKeys.next();
               selectedKeys.remove();

               Connection conn = (Connection)key.attachment();

               if(conn != null){
                   if(key.isValid() && key.isReadable()){
                                  //handle key reads
                   }
                   if(key.isValid() && key.isWritable()){
                          //handle key writes
                   }

                   continue;
               }

               if(key.isValid() && key.isAcceptable()){
                   Connection newConn = new Connection(transport, packetFactory, nextConnectionId);

                   try{
                       SelectionKey selectionKey = newConn.getConnection().handleAccept(selector, serverChannel.accept());
                       selectionKey.attach(newConn);

                            //send the client a connection message

                       nextConnectionId++;
                   }catch(IOException e){
                       newConn.close();
                       log.Error(e);
                   }
               }
           }

           long time = System.nanoTime();
           for(Connection c : connections){
               if(c.getConnection().isTimedOut(time)){
                   c.close();
                }

                if(c.needsPing(time)) {
                    c.sendPing();
                }
           }
        }catch(IOException e){
            log.Error(e);
        }
    }

handleAccept()方法看起来像这样

public SelectionKey handleAccept(Selector selector, SocketChannel ch) {

    try{
        socketChannel = ch;
        socketChannel.configureBlocking(false);
        lastReadTime = System.nanoTime();
        selectionKey = ch.register(selector, SelectionKey.OP_READ);

        return selectionKey;
    }catch(IOException e){
        close();
    }
}

handleAccept()方法是连接对象的一部分,它保存诸如连接socketChannel,selectionkey之类的信息。 每当连接被读取/写入时,这些更新如下所示:

public void handleWrite(SelectionKey key) throws IOException {
    socketChannel = (SocketChannel) key.channel();
    this.selectionKey = key;

    //do other stuff
 }

最后,当我们需要向客户端写入内容时,我们告诉socketChannel我们有兴趣编写代码,一旦我们写完了,我们会告诉它我们想要阅读。 这是通过访问连接选择键并将其interestOps()设置为正确的字段来完成的。

当我运行这个代码时,服务器执行以下操作:

[2014/05/25 01:49:23] [INFO] [com.adammflax.net.ServerEndPoint] Started Server on port 9001
[2014/05/25 01:49:26] [INFO] [com.adammflax.net.ServerEndPoint] Added the connection com.adammflax.net.Connection@4838ddcc to the list of connections on the server
[2014/05/25 01:49:26] [INFO] [com.adammflax.net.ServerEndPoint] Client com.adammflax.net.Connection@4838ddcc connected to the server given a Id of 0
30
[CLIENT RECEIVES CONNECTION PACKET]
[2014/05/25 01:49:26] [DEBUG] [com.adammflax.net.ServerEndPoint] Received ping from client now replying
[CLIENT RECEIVES Ping PACKET]

在这之后,服务器只是停留在selector.select()(它总会超时并返回0)。 任何人都可以向我解释为什么发生这种情况。 我知道这是很多需要深入研究的代码(并且试图压缩这些代码的缺失),但是对此问题的任何帮助都会令人惊叹。

编辑添加更多代码以显示我们如何检测ping是否需要发生(属于连接对象)

public boolean needsPing(long time) {
    if(!isConnected()){
        return false;
    }

    if(pingTime <= 0){
        return false;
    }

    if(time <= lastPingSentTime + pingTime){
        return false;
    }

    lastPingSentTime = System.nanoTime();

    return true;
}

发送ping将一个pingPacket对象转换为字节并调用send方法,该方法将字节添加到名为messages的messageQueue。

public void send(byte[] message) {
    messages.add(message);
    selectionKey.interestOps(SelectionKey.OP_WRITE);
}

因为我们现在在OP_WRITE中handleWrite最终会被调用

public void handleWrite(SelectionKey key) throws IOException {
    socketChannel = (SocketChannel) key.channel();

    if(socketChannel == null){
        key.cancel();
        throw new SocketException("Socket connection has closed!");
    }

    while(!messages.isEmpty()){
        try{
            writeBuffer = ByteBuffer.wrap(messages.peek());


            if(socketChannel.write(writeBuffer) == 0){
                break;
            }

            if (writeBuffer.remaining() > 0) {
                break;
            }

            writeBuffer.compact();
            messages.poll(); //everything went well so remove head
        }catch(IOException e){
            log.Error("failed to write : " + messages.peek() + " at " +
                    this.socketChannel.getRemoteAddress());
        }
    }   

    selectionKey.interestOps(SelectionKey.OP_READ);
}
链接地址: http://www.djcxy.com/p/34129.html

上一篇: Java nio server always stuck on selector.select

下一篇: Java nio, close socketChannel but leave ServerSocketChannel opened