使用套接字时的“流结束”是什么意思

2022-09-02 01:02:12

在Java中使用套接字时,在开始处理它们之前,您如何判断客户端是否已完成所有(二进制)数据的发送。例如,考虑:

istream = new BufferedInputStream (socket.getInputStream());
ostream = new BufferedOutputStream(socket.getOutputStream());

byte[] buffer = new byte[BUFFER_SIZE];

int count;
while(istream.available() > 0 && (count = istream.read(buffer)) != -1)
{
    // do something..
}

// assuming all input has been read
ostream.write(getResponse());       
ostream.flush();

我读过关于SO的类似文章,但找不到一个确凿的答案。虽然我上面的解决方案有效,但我的理解是,你永远无法真正判断客户端是否已经完成了所有数据的发送。例如,如果客户端套接字发送了几块数据,然后块等待来自另一个数据源的数据,然后才能发送更多数据,则上面的代码很可能假设客户端已完成所有数据的发送,因为 istream.available() 将为当前字节流返回 0。


答案 1

是的,你是对的 - 像这样使用是不可靠的。就个人而言,我很少使用。如果要阅读直到到达流的末尾(根据问题标题),请继续调用,直到它返回 -1。这是容易的一点。难点是,如果你不想要流的结束,而是“服务器此刻想发送给你的东西”的结尾。available()available()read()

正如其他人所说,如果您需要通过套接字进行对话,则必须使协议解释数据结束的位置。就个人而言,我更喜欢“长度前缀”解决方案,而不是“消息令牌结束”解决方案 - 它通常使读取代码变得简单得多。但是,它可能会使编写代码更加困难,因为在发送任何内容之前,您需要计算出长度。如果您可以发送大量数据,这将是一种痛苦。

当然,您可以混合和匹配解决方案 - 特别是,如果您的协议同时处理文本和二进制数据,我强烈建议使用长度前缀字符串,而不是以空终止它们(或任何类似的东西)。例如,如果您可以向解码器传递一个完整的字节数组并返回一个字符串,则解码字符串数据往往会容易得多 - 例如,您不必担心读取到字符的一半。你可以把它作为协议的一部分,但仍然有一个完整的“记录”(或你正在传输的任何东西),有一个“数据结束”记录,让读者处理数据并做出回应。

当然,如果你不能控制协议,所有这些协议设计的东西都是没有意义的:(


答案 2

我认为这更像是一个协议的任务,假设你是编写应用程序的发送端和接收端的人。例如,您可以实现一些简单的逻辑协议并将数据划分为数据包。然后将包装分成两部分:头部和身体。然后说你的头由一个预定义的起始序列组成,并在正文中包含字节数。忘记开始序列和简单的传输字节数在 bofy 作为数据包的第一个字节。然后你可以解决你的问题。


推荐