2017-10-18 183 views
0

我需要不斷地偵聽遠程套接字並對給定的輸入做出反應。Java socket偵聽器100%的CPU負載

public void listen(String ip, int port) { 
    try (
     Socket socketListener = new Socket(ip, port); 
     BufferedReader portReader = new BufferedReader(new InputStreamReader(socketListener.getInputStream())); 
    ) { 
     while (true) { 
     while (!portReader.ready()) { 
      // Wait for next PORT message 
     } 

     Logger.log(LogComponent.SOCKET, "Event received"); 
     } 
    } 
    } 

我在做什麼這麼大的錯誤,以上代碼使用100%的CPU負載?

在調試時我可以看到while-!portreader-loop是惡作劇者。但是我發現的大多數例子都是這樣做的。

編輯#1

考慮您的意見,我有如下的解決方案現在:

try { 

    Socket SocketListener = new Socket(ip, port); 
    BufferedReader portReader = 
    new BufferedReader(
     new InputStreamReader(SocketListener.getInputStream()) 
    ); 

    // We do not use common while(true)-pattern for reading the input. 
    // Instead, we check for new input 3 times a second. 
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(10); 
    executor.scheduleAtFixedRate(() -> { 
    try { 
     processInput(portReader); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    }, 0, 333, TimeUnit.MILLISECONDS); 

} catch (Exception exception) { 
    exception.printStackTrace(); 
} 

而且processInput(0)現在正在做的動作。 這結束比使用Thread.sleep()更好的性能結果 - 雖然我不明白爲什麼。

使用此方法時:代碼是否可能會遺漏來自套接字的某些消息?我的意思是在間隔期間?

+1

'while true'將始終以CPU允許的速度運行。因此需要一個「延遲」來減慢你的循環。計算每秒要檢查遠程套接字的頻率並插入適當的延遲。例如,等待1/10秒將導致每秒約10次檢查(減去檢查時間)。 –

+0

您還需要提供一個標記消息結束的字節。您的代碼被設計爲讀取的方式永遠不會返回-1,您將不得不關閉流以實現此目的。 – MissingSemiColon

+0

爲什麼你不能只執行阻塞閱讀?使用'Reader.ready()'來測試可讀性是一個錯誤的設計。如果您需要非阻塞行爲,請考慮使用SelectableChannel或Java 7中引入的異步I/O功能。 –

回答

0

問題是,你的代碼在你的while(true)中佔用了所有的CPU。作出這樣一個變化:因爲它的處理在while循環指令

public void listen(String ip, int port) { 
    try (Socket socketListener = new Socket(ip, port); 
    BufferedReader portReader = new BufferedReader(new InputStreamReader(socketListener.getInputStream()));) { 
     while (true) { 
      while (!portReader.ready()) { 
       // Wait for next PORT message 
       try { 
        Thread.sleep(1); 
       } catch(InterruptedException e) { 
        //handle InterruptedException 
       } 
      } 
      Logger.log(LogComponent.SOCKET, "Event received"); 
     } 
    } 
} 
+0

由於只有在服務器發送數據時,portReader.ready()才爲真,因此indle時CPU消耗相同。 –

0

你的CPU是繁忙的。

要避免它,你應該使用一個等待插座連接的函數。如果您正在等待傳入連接,請使用Socket.accept()。這將阻塞線程(即線程不會被調度執行)直到連接建立。

不要像其他人所建議的那樣使用Thread.sleep()。雖然這確實降低了CPU使用率,但它仍會不必要地燒燬CPU,並引入延遲。這是一個糟糕的工程實踐。

除此之外,您可能需要查看非阻塞或異步I/O。 See here瞭解更多信息。

+0

服務器幾乎立即接受套接字,這不是問題。插座然後不斷打開。但它每隔幾秒就發送一次數據。感謝您的鏈接! –

+0

在這種情況下,您可以嘗試'portReader.read()',它應該阻塞(釋放線程),直到輸入可用。至於你的EDIT#1,你現在已經在另一個線程上浪費了CPU,它仍然不是你正在尋找的解決方案,它實際上比以前更糟。 – jurez

+0

另請參閱https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html – jurez