如何在java中识别InputStream的结束
Posted
技术标签:
【中文标题】如何在java中识别InputStream的结束【英文标题】:How to identify end of InputStream in java 【发布时间】:2011-07-30 13:28:15 【问题描述】:我正在尝试使用 Socket 程序从服务器读取字节,即我正在使用 InputStream 读取字节。如果我通过长度大小,我可以读取字节,但我不确定长度可能是多少。所以我无法初始化字节数组。
我也试过while (in.read() != -1)
,我观察到它在发送数据时循环工作正常,但循环后的下一行无法执行,我觉得它仍在寻找流中的数据但没有数据。如果我关闭服务器连接,那么我的客户端将执行循环后面的下一行。
我不确定我哪里出错了?
this.in = socket.getInputStream();
int dataInt = this.in.read();
while(dataInt != -1)
System.out.print(","+i+"--"+dataInt);
i++;
dataInt = this.in.read();
System.out.print("End Of loop");
我得到的输出为:-
,1--0,2--62,3--96,4--131,5--142,6--1,7--133,8--2,9--16,10--48,11--56,12--1,13--0,14--14,15--128,16--0,17--0,18--0,19--48,20--0,21--0,22--0,23--0,24--0,25--1,26--0,27--0,28--38,29--114,30--23,31--20,32--70,33--3,34--20,35--1,36--133,37--48,38--51,39--49,40--52,41--49,42--55,43--49,44--52,45--52,46--54,47--55,48--50,49--51,50--52,51--48,52--53,53--56,54--51,55--48,56--48,57--57,58--57,59--57,60--57,61--57,62--57,63--57,64--56
但没有输出:- 循环结束
请指导我该如何关闭循环?
期待您的回复。提前谢谢大家。
【问题讨论】:
【参考方案1】:对于一次性 TCP 连接,这对我有用:
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
InputStream input = socket.getInputStream();
final byteSize = 1024;
byte[] data = new byte[byteSize];
int nRead;
while ((nRead = input.read(data, 0, data.length)) != -1)
buffer.write(data, 0, nRead);
if (nRead < byteSize)
break;
buffer.flush();
byte[] byteArray = buffer.toByteArray();
仅供参考,改编自Java InputStream to Byte Array and ByteBuffer。
【讨论】:
【参考方案2】:我遇到过类似的问题,in.read()
只是挂在数据末尾而不是返回 -1。
如果您无法控制服务器代码,是否可以在返回的数据中检测到可靠的标记(例如,</html>
)并使用它来结束读取循环?
如果做不到这一点,请考虑使用socket.setSoTimeout(reasonableValue)
,这样至少您的应用不会永远挂起...
【讨论】:
不,你没有。您在将消息结尾与流结尾混淆时遇到了问题。 我怎样才能检测到流的结束,所以我不会以in.read()
挂起我的应用程序结束?
@ban-geoengineering - 看看我的回答,或者 Jon Skeet 的,或者 Peter Lawrey 的。【参考方案3】:
我还有一个问题是我没有脱离循环。我的解决方案与此类似:
while (in.ready())
System.out.print(","+i+"--"+dataInt);
i++;
dataInt = this.in.read();
【讨论】:
只返回当前在套接字接收缓冲区中的任何内容。无法保证这是一条完整的消息。【参考方案4】:我认为您实际上已经回答了自己的问题。
您不退出循环的原因是输入流的结束仅在服务器端关闭其套接字后发生在客户端。 (或者更准确地说,在它关闭其套接字输出流......或等效......并且关闭事件已传播到客户端之后。)
在该事件发生之前,服务器可能会决定向套接字写入更多数据。所以客户端读取阻塞......直到它获得更多数据或它看到表明服务器端已关闭的协议事件。
(实际上,其他事件可以解除对客户端读取的阻塞,但它们都会导致某种IOException
,并且可能会导致您无法从中读取更多数据的流。)
现在,如果您不想现在关闭服务器端,因为您想稍后在套接字上发送更多内容,您将不得不更改您的应用程序协议以使用某种框架机制。例如,服务器可能会发送一个帧(或记录,或其他),该帧由字节数和给定的字节数组成。或者,它可以使用一个可区分的字节值或字节序列来标记一帧的结束。
【讨论】:
谢谢您的回复。如果我错了,请纠正我。我需要知道我将从 Socket 服务器接收的字节长度。这样我就可以根据循环跳出循环了。 @Vardhaman - 如果你想这样做,那么服务器必须先将字节长度发送给客户端。 谢谢各位,其实第一个两个字节给了我消息格式的长度,所以根据第一个两个字节我将识别字节数组,我需要处理错误【参考方案5】:如果是 -1,它不会结束循环。将 65535 置于条件中,我 99% 确信它会停止循环。
while(dataInt != 65535)
System.out.print(","+i+"--"+dataInt);
i++;
dataInt = this.in.read();
【讨论】:
【参考方案6】:您可以运行此示例。
如果您要等待流结束,您必须在发送端将其关闭以接收 EOF (-1)。
对于发送多个二进制消息,我更喜欢在消息之前发送长度,因为它允许代码一次读取大块(即,因为它知道它将获得多少)
ServerSocket ss = new ServerSocket(0);
Socket s = new Socket("localhost", ss.getLocalPort());
Socket s2 = ss.accept();
final OutputStream out = s.getOutputStream();
out.write("Hello World!".getBytes());
out.close();
final InputStream in = s2.getInputStream();
for (int b = 0; ((b = in.read()) >= 0);)
System.out.println(b + " " + (char) b);
System.out.println("End of stream.");
s.close();
s2.close();
ss.close();
打印
72 H
101 e
108 l
108 l
111 o
32
87 W
111 o
114 r
108 l
100 d
33 !
End of stream.
【讨论】:
抱歉,--
是一个非常糟糕的分隔符选择;)【参考方案7】:
它正在寻找更多数据,因为没有人告诉它不会有更多数据。网络的另一端可以随时发送更多数据。
尚不清楚您是在设计客户端/服务器协议还是只是尝试实现它,但通常有三种常用方法来检测消息的结尾:
在消息结束时关闭连接 将消息的长度放在数据本身之前 使用分隔符;一些在正常数据中永远不会出现的值(或者总是以某种方式被转义)我个人倾向于尽可能使用长度前缀;它使阅读代码变得更加简单,但仍然允许在同一连接上发送多条消息。
(此外,我同意 Daniel 的观点,即您应该使用 read
的重载,它一次读取整个缓冲区,而不是单个字节。这将更有效率 - 但不会从根本上改变您当前问题的性质。)
【讨论】:
我是试图将一些数据传递给服务器的客户端。服务器将以字节响应,我正在尝试读取这些字节。服务器已经由其他人创建和维护。如果我没记错的话,我怀疑服务器何时响应它必须关闭。另外我将如何关闭连接(客户端),因为我仍然无法确定我已阅读完整的响应 @Vardhaman:这完全取决于协议。我并不是建议 reading 端应该关闭连接 - writing 端(在这种情况下是服务器)应该关闭它的连接,此时你会看到数据的结尾。但这只有在协议是这样设计的情况下......它使用什么协议? 它的 TCP 我猜 :) 告诉我他们提供了端口和 IP 地址并告诉我通过 Socket 连接 @Vardhaman:这不是只是 TCP。涉及更高级别的协议,它描述了消息的格式、控制流等。你应该问“他们”预期的行为是什么。 @ikzjfr0:好吧,我不会仔细研究代码来了解如何。 TCP/IP 是一种基于流的协议;任何超过必须考虑的事情。现在,它上面的库可能会为您为添加消息长度前缀,但这并不能消除对它的需求......它只是为您完成。这些是非常不同的。【参考方案8】:您可以在while
循环之后安全地关闭流。 dataInt
为 -1 意味着没有更多可读取的内容。
http://download.oracle.com/javase/1.4.2/docs/api/java/io/InputStream.html#read()
如果 while 循环没有退出,则意味着在流的另一端仍有数据正在写入。关闭另一端的流。 如果您可以在将数据写入流的位置发布代码,那将很有帮助。
【讨论】:
我认为您误解了这个问题... dataInt 永远不是-1。 他正在从 Socket 读取 - 语义与 FileIO 不同 @Heiko Rupp:不,语义是相同的。当您到达流的末尾时,读取返回-1。期间。 EJP 不,他们不是。对于文件,您始终知道大小,因此您知道何时没有更多数据。对于 Sockets,如果遥控器没有提前明确发送大小或者它没有关闭套接字,你无法知道遥控器是否会向你发送更多。 @HeikoRupp 对于一个文件,如果它没有增加或者元数据在最后一次追加和最后一次检查之间被刷新,你总是知道它的大小,但这并没有改变事实与您的明确声明相反,文件的 读取语义 与套接字相同。以上是关于如何在java中识别InputStream的结束的主要内容,如果未能解决你的问题,请参考以下文章
如何将多个不同的InputStream链接到一个InputStream中