如何处理ServerSocketChannel.accept()IOException:NIO中打开的文件太多了?

时间:2015-09-13 05:48:35

标签: java sockets nio ioexception socketchannel

我的服务器有问题,星期五早上我收到了以下IOException:

11/Sep/2015 01:51:39,524 [ERROR] [Thread-1] - ServerRunnable: IOException: 
java.io.IOException: Too many open files
    at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) ~[?:1.7.0_75]
    at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241) ~[?:1.7.0_75]
    at com.watersprint.deviceapi.server.ServerRunnable.acceptConnection(ServerRunnable.java:162) [rsrc:./:?]
    at com.watersprint.deviceapi.server.ServerRunnable.run(ServerRunnable.java:121) [rsrc:./:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.7.0_75]

ServerRunnable类的第162行在下面的方法中,它是ssc.accept()调用。

private void acceptConnection(Selector selector, SelectionKey key) {

    try {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        SocketChannel sc = ssc.accept();
        socketConnectionCount++;

        /*
         * Test to force device error, for debugging purposes
         */
        if (brokenSocket
                && (socketConnectionCount % brokenSocketNumber == 0)) {

            sc.close();

        } else {

            sc.configureBlocking(false);
            log.debug("*************************************************");
            log.debug("Selector Thread: Client accepted from "
                    + sc.getRemoteAddress());

            SelectionKey newKey = sc.register(selector,
                    SelectionKey.OP_READ);
            ClientStateMachine clientState = new ClientStateMachine();
            clientState.setIpAddress(sc.getRemoteAddress().toString());
            clientState.attachSelector(selector);
            clientState.attachSocketChannel(sc);
            newKey.attach(clientState);

        }

    } catch (ClosedChannelException e) {

        log.error("ClosedChannelException: ", e);
        ClientStateMachine clientState = (ClientStateMachine)key.attachment();
        database.insertFailedCommunication(clientState.getDeviceId(),
                clientState.getIpAddress(),
                clientState.getReceivedString(), e.toString());
        key.cancel();

    } catch (IOException e) {
        log.error("IOException: ", e);

    }

}

我应该如何处理?阅读错误,它似乎是Linux操作系统中的一个设置,它限制了进程可以拥有的打开文件的数量。 从那以及this question here来看,似乎我没有正确关闭套接字(服务器当前正在为大约50个客户端提供服务)。这是一种情况,我需要一个计时器来监视打开的套接字并在一段时间后将它们计时?

在某些情况下,客户端可以连接,然后在建立连接后不发送任何数据。我以为我已经妥善处理了这些案件。

我的理解是,非阻塞的NIO服务器有很长的超时时间,如果我错过了这样的情况,它们可能会累积并导致此错误吗?

此服务器已运行三个月,没有任何问题。 在我查看代码并检查处理不当/丢失的情况后,处理此特定错误的最佳方法是什么?我还应该考虑其他可能有助于此的事情吗?

另外,(也许这应该是另一个问题)我有log4j2配置为发送错误和更高的日志级别的电子邮件,但我没有收到此错误的电子邮件。有什么理由可以吗?它通常工作,错误被记录到日志文件中,但我从未收到有关它的电子邮件。每次建立连接时都会发生错误,我应该已经充足了。

1 个答案:

答案 0 :(得分:2)

您修复了套接字泄漏。当您在套接字上获得EOS或IOException以外的任何SocketTimeoutException,时,您必须将其关闭。在SocketChannels,的情况下,这意味着关闭频道。仅仅取消关键,或忽略问题并希望它会消失,是不够的。连接已经消失。

您发现有必要计算损坏的套接字连接,并且捕获ClosedChannelException,已经表明您的应用程序中存在严重的逻辑问题。你不应该需要这个。取消封闭渠道的密钥并不能提供任何解决方案。

  

我的理解是,非阻塞的NIO服务器有很长的超时时间

非阻塞NIO服务器唯一的超时是您为select()指定的超时。内置于TCP堆栈的所有超时都不受您使用NIO还是非阻塞模式的影响。