当我的 InputStream 定期接收数据时,它的消息如何永远不会为空?

How my InputStream's message is never null when it is receiving data periodically?

我想要服务器-客户端关系。一旦 client 连接到 serverserver 每 1.5 秒向客户端发送一条消息。为简单起见,Server 不对来自 client 的消息执行任何操作。在 客户端的 代码中,我想使用 System.in 将消息发送到 服务器 (这没有任何意义),当我发送它,我想阅读 服务器的 响应(来自 服务器 的所有累积消息)。代码不是片段,而是“真正的代码”。

客户:

public class ClientClass {
    public static final String IP_ADDRESS = "127.0.0.1";
    public static final int PORT = 9090;

    public static void main (String[] args) throws IOException {
        Socket socket = new Socket (IP_ADDRESS,PORT);
        PrintWriter output = new PrintWriter (socket.getOutputStream (),true);
        BufferedReader input = new BufferedReader (new InputStreamReader (socket.getInputStream ()));
        BufferedReader keyboard = new BufferedReader (new InputStreamReader (System.in));

        while (true){
            System.out.println ("Enter message you want to send to server:");
            String msg = keyboard.readLine ();
            output.println (msg);

            String serverResponses = input.readLine ();
            while (serverResponses!=null){
                System.out.println (serverResponses);
                serverResponses=input.readLine ();
            }
        }
    }
}

服务器:

public class ServerClass {
    public static int PORT = 9090;

    public static void main (String[] args) throws IOException, InterruptedException {
        ServerSocket server = new ServerSocket (PORT);
        Socket client = server.accept ();
        PrintWriter output = new PrintWriter (client.getOutputStream (),true);
        while (true){
            output.println ("hello from server");
            Thread.sleep (1500);
        }
    }
}

//我的评论:client中的readLine()方法是blocking,所以看不懂服务器的消息,直到我真正从键盘上读到一些东西。这就是为什么我要循环“拾取”inputStream 中的所有行。我希望一旦我阅读了所有行,变量 stringResponses 就会得到 null 并从内部 while 循环中中断。然后我想我会在外循环中找到“输入要发送到服务器的消息”的打印到控制台。不幸的是,我的代码从未从内部 while 循环中跳出,我想知道为什么。当我每 1.5 秒写入一个流时,它怎么从来没有 null,这意味着它有足够的时间为 null 并从循环中中断。

实际输出:

Enter message you want to send to server:
hey! //this is what I typed
hello from server
hello from server
hello from server
//these three popped immediately, as they accumulated.
hello from server
hello from server
hello from server
hello from server
//keeps printing "hello from server" every 1.5s and never comes back to "enter your message" -WHY?

readLine return 流末尾为空。由于服务器套接字未关闭。它在你的内部循环中永远不会为 null。

Link - https://docs.oracle.com/javase/7/docs/api/java/io/BufferedReader.html#readLine()

A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached

readLine 在键盘对象的循环中被调用时,您期望什么行为?

String msg = keyboard.readLine();
while(msg != null)
   msg = keyboard.readLine();

这也将继续从 System.in 获取输入。循环永远不会结束。

解决方案

如上文 java 文档所述,readLine 方法块用于输入。这就是原因,没有延迟服务器响应的效果。方法 returns null 仅在流的末尾。

解决方案 1 - 利用就绪方法

ready 方法 returns false 当读取方法必须等待输入数据时。由于服务器代码休眠 1.5 秒,此方法在此期间应 return false。 这种方法的缺点是,如果服务器没有按时完成所有响应行,那么您收集所有服务器响应的内部循环将会中断。

while (true){
        System.out.println ("Enter message you want to send to server:");
        String msg = keyboard.readLine ();
        output.println (msg);

        String serverResponses = input.readLine ();
        System.out.println (serverResponses);

        while (input.ready()){
           serverResponses=input.readLine ();
           System.out.println (serverResponses);
        }
    }

解决方案 2 - 服务器发送总行数

如果服务器知道总行数,则可以将其作为流中的第一行发送。根据总行数,客户端代码可以读取那么多行。这种方法的缺点是必须在服务器响应中添加额外的一行。

while (true){
        System.out.println ("Enter message you want to send to server:");
        String msg = keyboard.readLine ();
        output.println (msg);

        Integer linesCount = Integer.parseInt(input.readLine ());
        for (int i=0; i< linesCount; i++){
            String serverResponses=input.readLine ();
            System.out.println (serverResponses);
            
        }
    }