NIO客户端给出exception:java.net.ConnectException:连接被拒绝:没有进一步的信息
我修改了此处可用于Client和Server My客户端的示例代码:
public class Client { public static void main(String[] args) { int n=10000; SocketTest [] st= new SocketTest[n]; for(int i=0;i<n;i++) st[i]= new SocketTest("hi"); for(int i=0;i<n;i++) new Thread(st[i]).start(); } } class SocketTest implements Runnable { private String message = ""; private Selector selector; private int i; public SocketTest(String message){ this.message = message; } @Override public void run() { SocketChannel channel; try { selector = Selector.open(); channel = SocketChannel.open(); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_CONNECT); channel.connect(new InetSocketAddress("127.0.0.1", 8511)); while (!Thread.currentThread().isInterrupted()){ selector.select(); Iterator keys = selector.selectedKeys().iterator(); while (keys.hasNext()){ SelectionKey key = keys.next(); keys.remove(); if (!key.isValid()) continue; if (key.isConnectable()){ connect(key); System.out.println("I am connected to the server"); } if (key.isWritable()){ write(key); } if (key.isReadable()){ read(key); } } } } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } finally { close(); } } private void close(){ try { selector.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void read (SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer readBuffer = ByteBuffer.allocate(1000); readBuffer.clear(); int length; try{ length = channel.read(readBuffer); } catch (IOException e){ System.out.println("Reading problem, closing connection"); key.cancel(); channel.close(); return; } if (length == -1){ System.out.println("Nothing was read from server"); channel.close(); key.cancel(); return; } readBuffer.flip(); byte[] buff = new byte[1024]; readBuffer.get(buff, 0, length); //length=buff.length; String fromserver = new String(buff,0,length,"UTF-8"); length = fromserver.length(); System.out.println("Server said: "+fromserver); key.interestOps(SelectionKey.OP_WRITE); } private void write(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); i++; message = "location now "+i; try{ Thread.sleep(5000); } catch(InterruptedException ie) { System.out.println(""+ie); } channel.write(ByteBuffer.wrap(message.getBytes())); // lets get ready to read. key.interestOps(SelectionKey.OP_READ); } private void connect(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); try { if(!channel.finishConnect()) System.out.println("* Here *"); } catch(ConnectException e) { System.out.println("BP 1"); e.printStackTrace(); //channel.close(); //key.cancel(); //return; } /*if (channel.isConnectionPending()){ while(!channel.ffinishConnect()){ System.out.println("not connected"); } }*/ channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_WRITE); } }
我通过创建多个线程在同一台机器上创建多个客户端。 线程数不是由n的值决定的。 当我运行少量客户端时,我遇到没有问题,但是一旦我用n运行500即500个客户端线程,一些线程正确运行但在某些我遇到这个: java.net.ConnectException: Connection refused: no further information at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source) at SocketTest.connect(Client.java:143) at SocketTest.run(Client.java:61)
第143行是: if(!channel.finishConnect())
因此,当我阅读此方法的文档时,它说它抛出:
NoConnectionPendingException – 如果未连接此通道且尚未启动连接操作。
ClosedChannelException – 如果此通道已关闭。
AsynchronousCloseException – 如果另一个线程在连接操作正在进行时关闭此通道。
ClosedByInterruptException – 如果另一个线程在连接操作正在进行时中断当前线程,从而关闭通道并设置当前线程的中断状态。
IOException – 如果发生其他一些I / O错误。
但Exception是ConnectException。 我尝试捕捉它,但它没有进入catch块。
任何帮助将不胜感激。 谢谢。 编辑:我在Windows上工作。 我尝试更改n的值,看看创建了多少客户端以及导致exception的数量,这些是结果(我知道在每次测试之后等待更多时间将允许更多的开放套接字,因为每个测试scokets将在TIME_WAIT之后发布):
n clients connected(by keeping a count at server) 1000 522 2000 568 3000 626 4000 600 (maybe I gave less time before successive runs) 5000 1345 6000 1389
我对如何只有这么多客户端连接起来有疑问。 任何人都可以建议更好的参考阅读Client Server NIO。
编辑2
正如EJP在评论中提到的那样,Backlog Queue窗口已经满了。 我修改了客户端代码以生成100个线程,然后hibernate5秒,这样,队列上没有太多负载,并且大部分连接都成功(但是当仍然有10,000个连接时,有些仍然失败)。
ConnectException: connection refused
意味着没有正在侦听IP:您尝试连接的端口,或者在服务器的listen-backlog队列填满的某些平台上。 如果它被抛出并且你正确地抓住它,你一定会抓住它。 您将不得不扩展实际发生的事情以及您的实际捕获代码的内容以获得进一步的帮助。
但是你还有很多其他问题:
private void connect(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); try { if(!channel.finishConnect()) System.out.println("* Here *");
此时,如果finishConnect()
返回false,则应返回。 您不应该通过并重新注册OP_WRITE.
的频道OP_WRITE.
该连接仍在等待中。 打印"* Here *"
也是徒劳的。 尝试打印有意义的东西
} catch(ConnectException e) { System.out.println("BP 1"); e.printStackTrace(); //channel.close();
你当然应该关闭频道。 它对人类或野兽没有任何用处。
//key.cancel();
关闭频道会取消该键。 删除遇到的任何地方。
//return;
如上所述,你当然应该回归。
} /*if (channel.isConnectionPending()){ while(!channel.ffinishConnect()){ System.out.println("not connected"); } }*/
摆脱这个问题。 在非阻塞模式下旋转循环永远不合适。 甚至不要把它留在周围作为评论:一些白痴可能会在以后出现并玩回来。
channel.configureBlocking(false);
该频道已处于非阻止模式。 否则你不会在这里。 去掉。
channel.register(selector, SelectionKey.OP_WRITE);
另一种方法是key.interestOps(SelectionKey.OP_WRITE);
睡在网络代码中实际上是浪费时间。 它没有解决任何问题。
你假设write()
完全成功,你忽略了它返回的计数。
您使用的是质量相当差的参考:
- 关于
write()
相同评论如上所述。 -
flip()
不像’重置’。 - 取消密钥会关闭频道。
- 您不必清除全新的
ByteBuffer,
但无论如何在每次读取时分配ByteBuffer
都是不好的做法。 -
ServerSocketChannel.accept()
可以返回null.
- 读取后显示String的代码不正确。
- 当密钥具有附件时,不需要使用
Map
。 - 无论如何,当NIO可中断时,无需继续测试
Thread.interrupted()
。 - 由于一个通道上存在一个
IOException
,因此无需关闭所有内容。
尝试找到更好的东西。
我相信你使用500个线程获得的ConnectException
不是来自SocketTest.connect()
。 它可能来自任何其他IO方法。
为了快速修复(并说服自己),您可以在主try-catch
块中显式捕获ConnectException
,如下所示:
try { // connect, write, and read ... } catch (ConnectException ce) { // <-- catch the more specific Exception first System.out.println("You caught a ConnectException."); } catch (IOException e1) { // <-- you originally only caught this // TODO Auto-generated catch block e1.printStackTrace(); } finally {
至于为什么会发生这种情况,我可以告诉你,我目前正在使用数百个线程来测试SOAP服务。 我也得到了整个地方的连接断开,所以也许可以预期这么多的并发线程。