2015-10-20 67 views
0

我知道這個問題已被問了好幾次了。然而,在遵循所有其他問題的建議之後,我仍然堅持問題是什麼。新ObjectInputStream導致掛起/超時

我有一個服務器客戶端。一個簡單的ping/pong程序。運行服務器,然後運行客戶端,並給它一些時間來運行它的過程中,超時異常開始被拋出時不時...

超時有防止塊,但是,如果刪除,它會導致程序停頓。

有沒有辦法來防止這種情況發生?

Server.java

public static void main(String args[]) { 
     try { 
      ServerSocket serverSocket = new ServerSocket(3000); 
      while (true) { 
       Socket socket = serverSocket.accept(); 
       String message = null; 
       try { 
        socket.setSoTimeout(3000); 
        try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) { 
         message = (String) objectInputStream.readObject(); 
         System.out.println("Server received " + message); 
        } 
        socket.close(); 
       } catch (IOException | ClassNotFoundException ex) { 
        //This exception is thrown because it hangs, but why does it hang? 
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
       } 
       if ((message != null) && (message.equals("Ping"))) { 
        try { 
         Socket pongSocket = new Socket("localhost", 3000); 
         pongSocket.setSoTimeout(3000); 
         try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(pongSocket.getOutputStream())) { 
          objectOutputStream.writeObject("Pong"); 
          objectOutputStream.flush(); 
          System.out.println("Server sent Pong"); 
         } 
         pongSocket.close(); 
         continue; 
        } catch (IOException ex) { 
         Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
        } 
       } 
      } 
     } catch (IOException ex) { 
      Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

Client.java

public static void main(String args[]) { 
     while (true) { 
      try { 
       Socket pingSocket = new Socket("localhost", 3000); 
       pingSocket.setSoTimeout(3000); 
       try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(pingSocket.getOutputStream())) { 
        objectOutputStream.writeObject("Ping"); 
        System.out.println("Client sent Ping"); 
        objectOutputStream.flush(); 
       } 
       pingSocket.close(); 
      } catch (IOException ex) { 
       Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 
    } 
+0

除了沖水()ANS迅速一部分,看看這個問題:http://stackoverflow.com/questions/33106127/swift-socket-readline-writeline/33106252#3310 6252。一旦serverSocket.accept()調用創建一個新的Socket,我更願意創建一個線程來處理IO。創建一個線程並傳遞客戶端Socket並在那裏執行你的IO –

回答

2

您對服務器和客戶端如何使用套接字感到困惑。你也許可以找到一堆的計算器和例子甚至可以通過谷歌更多,但總的成語是:

server: 
    create server socket 
    call accept on server socket 
    with accepted socket 
     read request from socket 
     write response to socket 
     close accepted socket 
     loop back to accept 

client: 
    create socket 
    call connect on socket 
    write request to socket 
    read response from socket 
    close socket 

(Java的自動完成一些人對你的,例如,創建套接字,並指定當主機和端口,Socket類調用connect for you。)

在您的服務器中,您在讀取請求後關閉接受的套接字,然後創建並連接到新的套接字以發送將要發送的響應localhost:3000上的任何監聽的響應,這是您的服務器的。另外,在你的客戶端,你正在寫請求,但沒有讀取響應,並且在一個緊密的循環中這樣做,所以你創建了很多連接到你的服務器,這將很快填補接受積壓。

真正的生產應用程序會在服務器中使用線程,甚至使用更高級別的庫或甚至像Tomcat這樣的整個服務器,但在底部,它們基本上都在做上述操作,所以很好理解這一點。

爲了證明,這是你的代碼應該是什麼樣子:

Server.java

public static void main(String args[]) { 
    try { 
     ServerSocket serverSocket = new ServerSocket(3000); 
     while (true) { 
      Socket socket = serverSocket.accept(); 
      socket.setSoTimeout(250); 
      String message = null; 
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 
      ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 
      try { 
       message = (String) objectInputStream.readObject(); 
       System.out.println("server read: " + message); 
      } catch (IOException | ClassNotFoundException ex) { 
       Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
      } 
      if ((message != null) && (message.equals("Ping"))) { 
       try { 
        objectOutputStream.writeObject("Pong"); 
        System.out.println("server sent pong"); 
       } catch (IOException ex) { 
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
       } 
      } 
      objectInputStream.close(); 
      objectOutputStream.close(); 
      socket.close(); 
     } 
    } catch (IOException ex) { 
     Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

Client.java

public static void main(String args[]) { 
    while (true) { 
     try { 
      Socket socket = new Socket("localhost", 3000); 
      String message; 
      socket.setSoTimeout(250); 
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 
      objectOutputStream.writeObject("Ping"); 
      System.out.println("client sent ping"); 
      try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) { 
       message = (String) objectInputStream.readObject(); 
       System.out.println("client read: " + message); 
      } 
      objectOutputStream.close(); 
      socket.close(); 
     } catch (IOException | ClassNotFoundException ex) { 
      Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex); 
     } 
     try { 
      Thread.sleep(10000); 
     } catch (InterruptedException ex) { 
      Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 
+0

那麼解決方案是什麼? – Hooli

+0

這不僅僅是一個簡單的修復,你需要修改你的代碼才能像上面那樣工作。例如,在Server.java中,讀取請求後,請不要立即關閉套接字,然後打開一個新套接字。相反,請檢查請求,然後發送適當的響應,然後關閉套接字並循環回接受。 – blm

+0

@ravindra感謝您做的研究,我手裏揮着「你可能會找到一堆例子」。 :-)我在這裏搜索過,但我在搜索中指定了Java,並且您指出的答案是關於Swift的問題,儘管答案是一個很好的一般答案,所以值得一看。 – blm

0

你有什麼節流您的發件人的速度。一眼就可以判斷,我認爲你只是通過發送請求的速度超過可以處理的速度來壓倒服務器。監聽套接字的「服務器」無法與非阻塞的「客戶端」競爭。