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

在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上阅读了类似的post,但是找不到确定的答案。 虽然上面的解决方案有效,但我的理解是,您永远无法确定客户端是否已完成所有数据的发送。 例如,如果客户端套接字发送了一些数据块然后阻止等待来自另一个数据源的数据,然后它可以发送更多数据,那么上面的代码可能很好地假设客户端已经完成了所有数据的发送,因为istream.available()将为当前字节流返回0。

是的,你是对的 – 使用像这样的available()是不可靠的。 我个人很少使用available() 。 如果要读取直到到达的末尾(根据问题标题),请继续调用read()直到它返回-1。 这很容易。 如果您不想要流的结束,那么硬位就是“服务器此刻想要发送给您的内容”的结尾。

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

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

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

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

正如一些人已经说过你无法避免某种通信协议。 它应该看起来像这样:

在服务器端,您有:

  void sendMSG(PrintWriter out){ try { //just for example.. Process p = Runtime.getRuntime().exec("cmd /c dir C:"); BufferedReader br = new BufferedReader(new InputStreamReader( p.getInputStream())); //and then send all this crap to the client String s = ""; while ((s = br.readLine()) != null) { out.println("MSG"); out.println(s); } } catch (Exception e) { System.out.println("Command incorrect!"); } out.println("END"); } //You are not supposed to close the stream or the socket, because you might want to send smth else later.. 

在客户端,您有:

 void recieveMSG(BufferedReader in) { try { while (in.readLine().equals("MSG")) { System.out.println(in.readLine()); } } catch (IOException e) { System.out.println("Connection closed!"); } } 

正如尼基塔所说,这更多是协议的任务。 您可以通过标题和正文方法进行操作,也可以为流末尾发送特殊字符或符号以中断处理循环。 如果你发送在套接字上说'[[END]]’来表示流的结尾。