20

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

4

4 回答 4

26

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

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

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

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

于 2009-03-16T06:28:07.970 回答
7

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

于 2009-03-16T05:15:19.427 回答
4

正如一些人已经说过的那样,您无法避免某种通信协议。例如,它应该如下所示:

在服务器端,您有:

 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!");
    }
  }
于 2011-06-09T08:48:09.647 回答
2

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

于 2009-03-16T05:21:12.860 回答