java - 为什么我的非阻塞 Java 服务器拒绝客户端连接?

标签 java sockets nio

大家好!我正在开发基于 NIO 的服务器,我正在尝试使用简单的客户端程序对其进行测试。

在发布代码之前,我想简要描述一下问题:在服务器立即完成工作的测试用例中,一切正常。但是,当我尝试添加一些现实生活中的行为(例如服务的短暂延迟)时,我收到“java.net.ConnectException:连接被拒绝”异常。更准确地说,100 个客户端线程中的一部分得到了这个连接被拒绝的异常。

我使用以下代码:

客户端

public class TCPClient implements Runnable{

private String name;

public TCPClient(String name)
{
  this.name = name;
}

public static void main(String[] args)
{

   for(int i=0;i<100;i++)
   {
        Thread t = new Thread(new TCPClient("thread # "+Integer.toString(i)));
        t.start();
   }
}

@Override
public void run()
{
   Socket socket = null;
   OutputStream out = null;
   int counter = 0;
   try
   {
        socket = new Socket();
        socket.connect(new InetSocketAddress("192.168.3.109",2345), 0); 
        out =  socket.getOutputStream();

        byte[] bytes;
        while(counter<100)
        {
            counter++;
            bytes = (name+ ", message # "+Integer.toString(counter)+System.lineSeparator()).getBytes();
            out.write(bytes); 
            out.flush();
            Thread.sleep(200);
        }
   }
   catch(Exception ex)
   {
            System.out.println(name+" "+Integer.toString(counter));
            ex.printStackTrace(new PrintStream(System.out));
            System.out.println();
   }
    finally
    {
        if(socket!=null && out!=null)
        {
            try
            {
                socket.close();
                out.close();
            }
            catch(Exception ex)
            {
                System.out.println("client close error");
            }
        }
    }
}

}

服务器

public class TCPServer  {

private Selector selector;
private boolean isRunning;
private ServerSocketChannel server;
private int counter;
private PrintWriter times;
private PrintWriter logger;
private Charset charset;
private CharsetDecoder decoder;
ByteBuffer bb;
long serviceTime,curTime;
Random random;
public TCPServer(int port)
{

    counter = 0;
    isRunning = false; 
    serviceTime = 0;
    random = new Random();
    random.setSeed(System.currentTimeMillis());
    bb =  ByteBuffer.allocate(2048);

try
{
    selector = Selector.open(); 
server = ServerSocketChannel.open(); 
server.socket().bind(new InetSocketAddress(port)); 
server.configureBlocking(false); 
server.register(selector, SelectionKey.OP_ACCEPT);


}
catch(Exception ex)
{
    System.out.println("initialization error "+ex.getMessage());

}

}


public void startServer() {
    isRunning = true;
    int acc = 0;
    boolean error = false;
    while (isRunning) {
       try 
       {


         selector.select(); 

         Set keys =  selector.selectedKeys();
         Iterator it = keys.iterator(); 
         while(it.hasNext())
         {
             SelectionKey key = (SelectionKey)it.next();

             if (key.isConnectable())
             { 
        ((SocketChannel)key.channel()).finishConnect(); 
     } 

             if (key.isAcceptable())
             { 
        //logger.println("socket accepted");
                    //logger.flush();
                    acc++;
                    System.out.println("accepted sockets count = "+acc);
                    SocketChannel client = server.accept(); 
                    client.configureBlocking(false); 
                    client.socket().setTcpNoDelay(true); 
                    client.register(selector, SelectionKey.OP_READ); 
     } 

             if (key.isReadable())
             { 

                   curTime = System.currentTimeMillis();
                   SocketChannel sc = (SocketChannel) key.channel();
                   bb.clear();
                   int x =  sc.read(bb); 

                   if(x==-1)
                   {
                       key.cancel();
                       continue;
                   }

                   counter++;

                  // Thread.sleep(2);
                   int sum=0;
                   for(int dummy=0;dummy<4000000;dummy++) // without this delay client works fine
                   {
                       sum+=random.nextInt();
                       sum%=1005;
                   }

                   serviceTime+= System.currentTimeMillis() - curTime;
                   if(counter>=10000)
                   {
                        System.out.println("recieved messages count = "+counter);
                        System.out.println("service time = "+serviceTime+" milliseconds");
                   }


     } 
         }
         keys.clear();
  } 
  catch (Exception ex) 
  {     

      System.out.println("error in recieving messages "+ex.getMessage());

  }

 }
}

public static void main(String[] args)
{

   TCPServer deviceServer = new TCPServer(2345);
   deviceServer.startServer();

}
}

问题出在 for(dummy...) 循环中——它只是模拟服务延迟——解析传入消息、向数据库写入内容等所需的时间。当延迟很小时代码工作正常,所有 10000 条消息都会到达服务器(100 个客户端线程 X 来自每个客户端的 100 条消息)但是当虚拟循环进行超过 3.000.000 次迭代时,一些客户端线程无法连接到服务器。这里还有一件奇怪的事情是客户端套接字忽略了无限超时属性。我的意思是超时等于零的 socket.connect(InetAddress,timeout) 意味着无限超时——换句话说,服务延迟没有意义,至少我期望这样的行为。

最佳答案

看起来服务器套接字有它允许的最大挂起连接数。 JavaDoc for ServerSocket说:

The maximum queue length for incoming connection indications (a request to connect) is set to 50. If a connection indication arrives when the queue is full, the connection is refused.

现在,我找不到关于 ServerSocketChannel 的相同信息,但我确定它一定存在。

ServerSocketChannel.bind允许配置允许的挂起连接数。

关于java - 为什么我的非阻塞 Java 服务器拒绝客户端连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16432817/

相关文章:

java - 将 FloatBuffer 转换为 ByteBuffer

java - Java 上的文件存储 - FS block 大小对齐有意义吗?

java - 将位图转换为 uri

java原始数组到JSONArray

远程计算机上的 Java 登录用户名

c - 格式错误的 DNS 响应

java - 如何在 UML 枚举中对覆盖进行建模?

java - 无法在 Mac OS X 上通过套接字进行通信

c# - 如何使用套接字通过 Internet 发送数据?

java - 如何读取特定格式的内存映射文件?