2016-03-07 47 views
1

以下程序作为TCP客户端,并使用NIO打开套接字连接到远程服务器,如下的Java NIO客户端会导致文件描述符泄漏,只有当远程TCP服务器宕机

private Selector itsSelector; 
private SocketChannel itsChannel; 

public boolean getConnection(Selector selector, String host, int port) 
{ 
    try 
    { 
     itsSelector = selector; 
     itsChannel = SocketChannel.open(); 
     itsChannel.configureBlocking(false); 
     itsChannel.register(itsSelector, SelectionKey.OP_CONNECT); 
     itsChannel.connect(new InetSocketAddress(host, port)); 
     if (itsChannel.isConnectionPending()) 
     { 
      while (!itsChannel.finishConnect()) 
      { 
       // waiting until connection is finished 
      } 
     } 
     itsChannel.register(itsSelector, SelectionKey.OP_WRITE); 
     return (itsChannel != null); 
    } 
    catch (IOException ex) 
    { 
     close(); 
     if(ex instanceof ConnectException) 
     { 
      LOGGER.log(Level.WARNING, "The remoteserver cannot be reached"); 
     } 
    } 
} 

public void close() 
{ 
    try 
    { 
     if (itsChannel != null) 
     { 
      itsChannel.close(); 
      itsChannel.socket().close(); 
      itsSelector.selectNow(); 
     } 
} 
    catch (IOException e) 
    { 
     LOGGER.log(Level.WARNING, "Connection cannot be closed"); 
    } 
} 

这个程序在Red Hat运行企业Linux服务器版本6.2(Santiago) 当并发套接字的数量处于建立阶段时,文件描述符限制达到最大值,并且在尝试建立更多套接字连接时看到下面的异常。

java.net.SocketException: Too many open files 
       at java.net.PlainSocketImpl.socketAccept(Native Method) 
       at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408) 

只有当远程节点关闭时,这种情况才会发生,并且当它启动时,一切正常。 当远程TCP服务器关闭,如在上面的代码

java.net.ConnectException: Connection refused: no further information 
    at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) 
    at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source) 

如IOException异常处理以下异常被抛出有什么办法强行关闭了底层的文件描述符在这种情况下。 在此先感谢您的帮助。

+1

为什么要配置阻塞false然后阻塞呢? – Neijwiert

+0

根据Javadoc的说法,它应该由nio库自动关闭:“如果连接尝试失败,也就是说,如果调用这个方法抛出一个检查的异常,那么通道将被关闭。” –

+0

@ErwinBolwidt澄清,该文本来自'finishConnect()'的描述,它甚至不应该在这里被调用。 – EJP

回答

1
private Selector itsSelector; 

我看不到这个声明的要点。您可以随时获得频道注册的选择器,如果您需要,您永远不会做。可能你正在泄漏选择器?

itsChannel.configureBlocking(false); 
itsChannel.register(itsSelector, SelectionKey.OP_CONNECT); 

这里您注册为OP_CONNECT,但从来没有作出丝毫设施的使用。

itsChannel.connect(new InetSocketAddress(host, port)); 

在这里您正在启动挂起的连接。

if (itsChannel.isConnectionPending()) 

它是。你刚开始。测试毫无意义。

{ 
    while (!itsChannel.finishConnect()) 
    { 
     // waiting until connection is finished 
    } 
} 

这只是时间和空间的完全浪费。如果您不希望使用选择器检测OP_CONNECT何时启动,则应在之前先拨connect()将通道设置为非阻塞状态,然后摆脱这种毫无意义的测试和循环。

itsChannel.register(itsSelector, SelectionKey.OP_WRITE); 
    return (itsChannel != null); 

itsChannel此时不可能为空。测试毫无意义。你最好允许可能出现的IOExceptions传播出这种方法,以便调用者可以了解失败模式。这也使得主叫方有责任关闭的任何例外,而不仅仅是你在这里捕捉的。

catch (IOException ex) 
{ 
    close(); 
    if(ex instanceof ConnectException) 
    { 
     LOGGER.log(Level.WARNING, "The remoteserver cannot be reached"); 
    } 
} 

请参阅上文。删除所有这些。如果要区分ConnectException与其他IOExceptions,分别捕获它。而且你忘记记录任何不是 a ConnectException

public void close() 
{ 
    try 
    { 
     if (itsChannel != null) 
     { 
      itsChannel.close(); 
      itsChannel.socket().close(); 
      itsSelector.selectNow(); 

第二个close()呼叫是毫无意义的,因为通道已经关闭。

catch (IOException e) 
{ 
    LOGGER.log(Level.WARNING, "Connection cannot be closed"); 
} 

我很高兴看到你最后登录的IOException,但你可能不会得到任何这里。

不要这样写代码。

+0

“我很高兴见到你终于登录了一个IOException,但你不可能在这里得到任何东西。”不太可能,但仍然有可能,但是如果失败,你的程序应该中止:)。好答案。 – Neijwiert

+0

在我的情况下,文件描述符泄漏的根本原因是Selector未关闭,当连接无法建立或远程服务器关闭时。当关闭getConnection方法的catch块中的选择器时,问题就解决了 –

+0

@SupriyaKulkarni这是一个糟糕的解决方案。您正在关闭其他人提供的Selector。实际的问题是其他人创造无尽的选择器。您只需要一个该程序的生命。 – EJP