以下程序充当TCP客户端,并使用NIO打开到远程服务器的套接字,如下所示
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 Enterprise Linux Server 6.2版(圣地亚哥)上运行 当并发套接字的数量处于建立阶段时,文件描述符限制达到最大值,我在尝试建立更多套接字连接时看到异常。
java.net.SocketException: Too many open files
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
仅当远程节点关闭时才会发生这种情况,并且当它启动时,一切都很好。 当远程TCP服务器关闭时,抛出异常,因为在上面的代码
中处理为IOExceptionjava.net.ConnectException: Connection refused: no further information
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source)
在这种情况下,有没有办法强制关闭底层文件描述符。 提前感谢所有的帮助。
答案 0 :(得分:2)
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
不可能为null。测试毫无意义。你最好允许IOExceptions
传播出这种方法,以便调用者可以了解失败模式。这也使得调用者有责任关闭任何异常,而不仅仅是你在这里捕获的异常。
catch (IOException ex)
{
close();
if(ex instanceof ConnectException)
{
LOGGER.log(Level.WARNING, "The remoteserver cannot be reached");
}
}
见上文。删除所有这些。如果您想区分ConnectException
与其他IOExceptions
,捕获,请单独区分。并且您忘记记录不是 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
,但你不太可能在这里找到。
不要写这样的代码。