2017-10-18 178 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