【问题标题】:Reading Bytes from an InputStreamReader goes into infinite loop从 InputStreamReader 读取字节进入无限循环
【发布时间】:2020-01-17 17:17:41
【问题描述】:

我的测试失败了,因为从 InputStreamReader 读取字节会进入无限循环。

@Test
public void testSingleRequest() throws IOException {
        Server server = new Server(9000, 100);
        new Thread(server).start();
        Socket clientSocket = new Socket("localhost", 9000);
        OutputStream out = clientSocket.getOutputStream();
        InputStream in = clientSocket.getInputStream();
        String payload = "a";
        byte[] load = buildPushPayload(payload);
        out.write(load);
        byte[] response = IOUtils.toByteArray(in);
        server.stop();

        assertEquals(0, response[0]);


    }

这是写入 InputStream 的代码

byte[] resp = buildPushResponse();
output.write(resp);

private byte[] buildPushResponse() throws UnsupportedEncodingException {
        ByteBuffer buffer = ByteBuffer.allocate(1);
        buffer.order(ByteOrder.BIG_ENDIAN);
        buffer.putInt(0);
        return buffer.array();
    }

服务端和客户端都写入clientSocket输入输出流。 我似乎无法理解为什么会出现无限循环?

【问题讨论】:

  • 我认为“无限循环”是错误的术语。我认为 testSingleRequest 在IOUtils.toByteArray(in) 上是 blocking - 您要求将整个流转换为字节数组。所以, toByteArray 一直在等待,直到输入流为 EOF。您还没有告诉它要读取多少字节,并且套接字仍然打开。相反,您可能想要检查 in.available(),并读取那么多字节。如果您正在寻找基于消息的套接字,您可能想尝试 websockets。
  • @Jamie 我是新手,你能帮我这里的代码吗?你是在建议while(in.available()){ byte[] response = IOUtils.toByteArray(in);}
  • 我还应该关闭服务器输出流以使输入流对客户端不可用吗?
  • 这里没有InputStreamReader。或者任何无限循环。您不会将字节写入InputStream,也不会从Reader 读取字节:您读取字符;而且这个协议不包含字符,它包含二进制数据。当您使用错误的术语并做出虚假声明时,即使不是不可能,也很难理解您在说什么。你应该使用DataInputStream。是的,您应该关闭发送方的输出流,否则您将永远不会在接收方收到 EOS。
  • 但是你可以扔掉所有这些代码并分别使用new DataOutputStream(socket.getOutputStream()).writeInt(0)int resp = new DataInputStream(socket.getInputStream().readInt()

标签: java sockets inputstream outputstream


【解决方案1】:

您在这里缺少的是如何知道何时停止阅读。您正在调用IOUtils.toByteArray(in),它将一直读取到文件结束,这永远不会发生(除非另一方关闭其套接字末端)。

在处理完整的消​​息包之前,您可以使用一些方案来了解要从套接字读取多少字节作为消息包。例如,您可以说所有数据包都是 8 个字节(在这种情况下,您的 read 调用将指定读取 8 个字节),您可以说第一个字节是数据包中的字节数,您可以说一个数据包是可变长度,并以回车字节结束。

一旦您读取了一个数据包,您通常会处理该数据包,然后返回读取另一个数据包或关闭套接字(如果您已完成)。

这是一个很好的套接字编程示例:https://www.geeksforgeeks.org/socket-programming-in-java/

在这种情况下,他们使用 End Of Line 标记终止数据包或消息 - 他们一次读取一行文本,使用 DataInputStream.readUTF(socket)

server = new ServerSocket(port); 
socket = server.accept(); 

// Create a DataInputStream object that takes input from the socket
in = new DataInputStream(new BufferedInputStream(socket.getInputStream())); 

String message = ""; 

// reads messages from client and prints them
// The loop exits if the message "Over" is received.
try
{ 
    while (!"Over".equals(message))
    { 
        message = in.readUTF(); 
        System.out.println(message); 
    }
}
catch(IOException i) 
{ 
    // handle error
} 
// close connection 
socket.close(); 
in.close(); 

【讨论】:

  • 这段代码将在流的末尾永远循环,因为它不能正确处理EOFException,即break
  • 已修改!我愚蠢地遵循了我建议的网站上的例子。
  • 要正确执行此操作,您必须单独捕获EOFException,因为您可能不想记录它,然后是您可能确实想要记录的IOException。可怕的链接。异常被捕获并打印出来,然后代码继续执行,就好像它们从未发生过一样。它无法从readLine() 检查null。 EOS问题如上。冗余关闭。在构造函数中执行阻塞 I/O。 Socket 作为具有ServerSocket 的同一类的数据成员。 System.in 已关闭。 ServerSocket 永远不会关闭。真正可怕的代码。不要使用这些 tinpot 网站。
猜你喜欢
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
  • 2013-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-25
相关资源
最近更新 更多