2013-05-08 93 views
2

大家好日子!我正在開發基於NIO的服務器,並試圖用簡單的客戶端程序對其進行測試。爲什麼我的非阻塞Java服務器拒絕客戶端連接?

在發佈代碼之前,我想簡單描述一下問題:在測試用例中,服務器立即執行他的工作一切正常。但是,當我試圖添加一些真實的生活行爲,如服務時間短,我得到「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(); 

} 
} 

的問題是在(僞...)循環 - 這是服務的延遲只是模擬 - 解析傳入消息所需的時間,向DB寫入內容等等。當延遲很小時,代碼工作正常,所有10000條消息到達服務器(100個客戶端線程X 100個來自每個客戶端的消息),但當虛擬循環超過3.000.000次迭代時,某些客戶端線程無法連接到服務器。另一個奇怪的事情是忽略客戶套接字的無限超時屬性。我的意思是socket.connect(InetAddress,timeout)的timeout等於零意味着無限超時 - 換句話說服務延遲沒有意義,至少我期望這種行爲。

+0

「連接拒絕」異常是否立即發生或在客戶端發生延遲後發生? – Uooo 2013-05-08 04:46:44

+0

不立即。一些客戶端線程設法連接到服務器,其他客戶線程在短時間後會出現異常。 – Baurzhan 2013-05-08 04:54:41

+0

什麼是「短時間之後」?幾秒,幾分鐘,幾毫秒? – Uooo 2013-05-08 05:05:41

回答

2

它看起來像服務器套接字有最大數量的待定連接,它將允許。所述JavaDoc for ServerSocket說:

傳入連接指示(一個 請求連接)如果連接指示到達 當隊列滿被設置爲50。最大隊列長度,連接被拒絕。

現在,我找不到 ServerSocketChannel的相同信息,但我確定它必須存在。

ServerSocketChannel.bind允許配置允許的掛起連接的數量。

+2

您的客戶端連接速度比服務器接受的速度快,因此最終還是有很多人(JavaDoc沒有說多少,但我猜想50,因爲這是Java 6中的)。解決這個問題的一種方法是創建兩個線程,一個用於接受新連接,另一個用於讀寫這些連接(您的演示延遲將會在此處)。 – SimonC 2013-05-08 05:28:33

+0

謝謝,西蒙!你的建議幫助了我。 – Baurzhan 2013-05-08 05:30:36

+0

它們都允許配置積壓隊列的長度。 – EJP 2013-05-08 08:42:42

相關問題