2

大家好!我正在开发基于 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...) 循环中 - 它只是模拟服务延迟 - 解析传入消息所需的时间,将某些内容写入 DB 等等。当延迟很小的代码工作正常时,所有 10000 条消息都到达服务器(100 个客户端线程 X 来自每个客户端的 100 条消息)但是当虚拟循环进行超过 3.000.000 次迭代时,一些客户端线程无法连接到服务器。这里还有一件奇怪的事情是忽略客户端套接字的无限超时属性。我的意思是 socket.connect(InetAddress,timeout) 超时等于零意味着无限超时 - 换句话说,服务延迟没有意义,至少我期望这种行为。

4

1 回答 1

2

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

传入连接指示(连接请求)的最大队列长度设置为 50。如果在队列已满时连接指示到达,则拒绝连接。

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

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

于 2013-05-08T04:57:45.483 回答