使用套接字时,“流结束”意味着什么

时间:2009-03-16 05:04:49

标签: java sockets network-programming

使用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();

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

4 个答案:

答案 0 :(得分:23)

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

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

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

当然,如果您无法控制协议,所有这些协议设计都没有实际意义:(

答案 1 :(得分:7)

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

答案 2 :(得分: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!");
    }
  }

答案 3 :(得分:2)

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