4

我正在尝试使用基于“Rox Java NIO 教程”中的 java NIO(非阻塞)的服务器端代码。有很多传入的套接字连接,我只想接受 100 个。因此,如果有 100 个活动连接,则应该拒绝/拒绝新连接。但是怎么做呢?只有方法 ServerSocketChannel.accept() 返回 SocketChannel 对象。使用该对象我可以调用 socketChannel.socket().close(),但连接已经打开。以下是部分代码:

@Override
public void run() {
    while (true) {
        try {
            // Wait for an event one of the registered channels
            this.selector.select();

            // Iterate over the set of keys for which events are available
            Iterator selectedKeys = this.selector.selectedKeys().iterator();
            while (selectedKeys.hasNext()) {
                SelectionKey key = (SelectionKey) selectedKeys.next();
                selectedKeys.remove();

                if (!key.isValid()) {
                    continue;
                }

                // Check what event is available and deal with it
                if (key.isAcceptable()) {
                    this.accept(key);
                } else if (key.isReadable()) {
                    this.read(key);
                } else if (key.isWritable()) {
                    this.write(key);
                }
            }
        } catch (Exception e) {
            logger.warn("Reading data", e);
        }
    }
}

和 accept() 方法:

 private void accept(SelectionKey key) throws IOException {
    // For an accept to be pending the channel must be a server socket channel.
    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

    // Accept the connection and make it non-blocking        
    if (noOfConnections < MAX_CONNECTIONS) {
        SocketChannel socketChannel = serverSocketChannel.accept();
        Socket socket = socketChannel.socket();
        socket.setKeepAlive(true);
        socketChannel.configureBlocking(false);
        // Register the new SocketChannel with our Selector, indicating
        // we'd like to be notified when there's data waiting to be read
        socketChannel.register(this.selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);//listener for incoming data: READ from client, WRITE to client
        noOfConnections++;
        logger.info("Accepted: " + socket.getRemoteSocketAddress().toString());
    } else {

        // REJECT INCOMING CONNECTION, but how?

        logger.warn("Server is full: " + noOfConnections + " / " + MAX_CONNECTIONS);
    }
}

如果连接不被接受,那么 accept() 方法被一遍又一遍地调用。

感谢帮助!

4

3 回答 3

1

没有办法做到这一点,但我怀疑那是你真正想要的,或者至少是你真正应该做的。

如果您想停止接受连接,请将服务器套接字通道的选择键中的 interestOps 更改为零,并OP_ACCEPT在您准备好再次接受时将其更改回。在此期间,isAcceptable()永远不会是真的,所以你描述的问题不会发生。

但是,这不会导致进一步的连接被拒绝:它只会将它们留在积压队列中,我认为它们属于 TCP 设计者。如果积压队列被填满,将会出现另一种失败行为:它在客户端中的影响取决于系统:连接拒绝和/或超时。

于 2013-01-29T15:58:30.140 回答
0

我认为对积压队列的任何调整几乎都不是一个好的解决方案。但也许,你可以停止听。

于 2013-01-29T21:18:41.217 回答
0

好吧,我用下一个方法解决了这个问题:套接字上的挂起状态连接是一种“中间状态”,这意味着你不能控制/拒绝它们。特定 VM 可能以不同的方式使用/忽略/处理积压套接字参数。这意味着您必须接受特定的连接才能接收关联的对象并对其进行操作。

使用一个线程接受连接,将接受的连接传递给第二个线程进行处理。为活动连接数创建一些变量。现在,当活动连接的数量小于希望的最大值时,接受连接,将数量增加 1,然后传递给第二个线程进行处理。否则,接受连接并立即关闭它。

此外,在连接进程线程中,比完成时,将活动连接数减少 1 以指向还有一个可用的空闲通道。

EDT:刚刚为 Java.Net NIO 制作了服务器机制的“存根”。可适应 OP 需求:

package servertest;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;


public class Servertest extends Thread {
    final int MAXIMUM_CONNECTIONS = 3;
    int connectionnumber = 0;


    /**
     * @param args the command line arguments
     * @throws java.io.IOException
     */
    public static void main(String[] args){

        new Servertest().start();
    }

    @Override
    public void run() {
        try {
            ServerSocket sc = new ServerSocket(33000, 50, InetAddress.getLoopbackAddress());
          while (sc.isBound()) {
            Socket connection = sc.accept();
            if(connectionnumber<=MAXIMUM_CONNECTIONS){
                new ClientConnection(connection).start();
                connectionnumber++;
            } else {
                //Optionally write some error response to client
                connection.close();
            }
          }
        } catch (IOException ex) {
            Logger.getLogger(Servertest.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private class ClientConnection extends Thread{
        private Socket connection;
        public ClientConnection(Socket connection) {
            this.connection=connection;
        }

        @Override
        public void run() {
            try {
                //make user interaction

                connection.close();


            } catch (IOException ex) {
                Logger.getLogger(Servertest.class.getName()).log(Level.SEVERE, null, ex);
            }

            connectionnumber--;

        }


    }

}
于 2015-10-09T12:08:45.457 回答