Java NIO:serverSocketChannel 接受套接字请求,客户端收到接受但服务器不记录它

Java NIO: A serverSocketChannel accepts a socket request and client receives the acceptance but the server does not log it

我正在尝试开发我自己的基于非阻塞 NIO 消息的通信库。我已经阅读了 1000 篇关于它的教程和书籍章节,我认为最后我得到了一些同时连接很少的东西。但是当我在服务器端共存许多连接时,我遇到了一些问题。

我有 4 个私有方法的典型选择器实现:accept、finishConnect、read 和 write。我的问题在于前两个:Accept 和 finishConnect。

当客户端打开一个新的socket,一个可接受的key唤醒selector,执行下面的代码。

private void accept(SelectionKey key) {
    try {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        SocketChannel sc = ssc.accept();
        sc.configureBlocking(false);
        LOGGER.debug("Socket " + sc.hashCode() + "-" + sc.socket().toString() + " connexion completed");
        changeInterest(sc, SelectionKey.OP_READ);
        eventManager.addEvent(new ConnectionEstablished(sc));
    } catch (Throwable e) {
        NIOException ne = new NIOException(NIOException.ErrorType.ACCEPTING_CONNECTION, e);
        eventManager.addEvent(new ErrorEvent(null, ne));
    }
}

在客户端,我有连接方法的这个实现,一旦服务器处理了它可接受的套接字密钥,就会调用该方法。

private void finishConnect(SelectionKey key) {
    SocketChannel sc = (SocketChannel) key.channel();
    try {
        if (sc.finishConnect()) {
            eventManager.addEvent(new ConnectionEstablished(sc));
            LOGGER.debug("Socket " + sc.hashCode() + "-" + sc.socket().toString() + " connection finished");
        } else {
            LOGGER.debug("REFUSED " + sc + " - " + sc.socket().toString());
            refusedConnection(sc, null);
            key.cancel();
        }
    } catch (Exception e) {
        refusedConnection(sc, e);
        key.cancel();
    }
}

事情是当我创建一些连接被接受时,客户端执行 finishConnect 消息(我可以看到使用端口建立的套接字连接)。但是我在服务器端找不到这个连接接受,没有使用这些端口的连接完成日志消息!!

我怀疑 ssc.accept() 和日志调用之间可能会出现异常,因此我添加了一些额外的日志消息来检查是哪条指令导致一切崩溃。对于进入 accept 方法的所有键,序列已完成。

如果我什至在日志中看不到任何错误消息,那怎么可能呢?

编辑:我对一次打开的套接字数量进行了一些测试。当客户端启动运行时,服务器上只有一个openSocket:服务器套接字。之后它有多达 200 个同时打开的套接字,并且在客户端执行结束时服务器返回到 1 个打开的套接字。我猜他们永远不会被计算在内

到目前为止,我已经制定了一个变通方法来监视节点上的共存连接数量并延迟接受新连接,直到该数量减少到给定阈值。但是我想了解发生了什么问题。

感谢您的帮助。

由于积压队列,在执行 accept() 之前完成大量客户端连接是完美的。所以这里没有实际问题要解决。

但是你曾经执行过accept()方法吗?这是您需要调查的错误。

正如 EJP 所说,问题出在积压队列上。我使用 bind(SocketAddress local) 方法绑定 ServerSocketChannel。

当套接字请求到达JVM时,它被排入积压队列并在那里等待,直到Listener触发相应键的过程被接受。实际问题在于这个队列的大小,使用bind方法,最多存储50个连接。

当出现连接请求高峰时,队列会溢出,其中一些会丢失。为避免这种情况发生,方法 bind(SocketAddress local, int backlog) 允许更改队列的容量并增加它。

另一方面,在非阻塞模式下工作时,客户端节点上的选择器不需要接受连接来处理OP_CONNECT键。接收到SYN-ACK TCP报文会触发selector中对应的key。