Java Socket问题:接收端合并报文.处理步骤



我有一个插座问题。当我在同一台PC上运行服务器和客户端时,即使用"localhost"参数,就会出现这个问题。但是当不同的个人电脑同时使用时,问题就不存在了。客户端发送一个包含以下代码的文件:

output_local.write(buffer, 0, bytesRead);
output_local.flush();

之后,在另一个方法中,我用这些发送命令:

outputStream.write(string);
outputStream.flush();

Server将命令附加到文件末尾。所以它认为它还没有收到来自客户端的命令。你知道是什么导致了这个问题吗?我该如何解决这个缺陷?下面是服务器上的文件接收方法:

    while (true) {
        try {
            bytesReceived = input.read(buffer);
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("exception occured");
            break;
        }
        System.out.println("received:" + bytesReceived);
        try {
            /* Write to the file */
            wr.write(buffer, 0, bytesReceived);
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
        total_byte = total_byte + bytesReceived;
        if (total_byte >= filesizeInt) {
            break;
        }
    }

如果你想要类似消息的支持,你需要创建一个协议来明确你将要发送和接收的内容。

在TCP中,你不能依赖于单独的"数据包"被单独接收(例如,发送4个10字节的块可能被接收为1个40字节的块,或2个20字节的块,或一个39字节的块和一个1字节的块)。TCP保证有序交付,但不保证任何特定的数据"打包"。

例如,如果你要发送一个字符串,你需要先发送字符串的长度,然后是它的字节数。伪代码中的逻辑类似于:

客户:

  1. 发送命令指示符
  2. 发送有效载荷长度
  3. 发送有效载荷

服务器:

  1. 读取命令指示符
  2. 读取有效载荷长度
  3. 循环读取负载,直到读取完整长度

缺陷在于您将基于流的协议(TCP)视为面向消息的协议。它不是。你应该假设这是可能发生的。

如果您需要将您的流分解为单独的消息,您应该为每个消息使用分隔符或(最好是IMO)长度前缀。您还应该预料到,您发出的任何读取可能没有收到您所要求的那么多数据——换句话说,如果您不小心的话,不仅可以将消息组合在一起,而且还可以很容易地将它们分开。

我提到过我更喜欢长度前缀而不是分隔符。利弊:

  • 使用消息分隔符的好处是在开始发送之前不需要知道消息的大小。
  • 使用长度前缀的好处:
    • 读取消息的代码根本不需要关心消息中的数据—它只需要知道它有多长。读取消息长度,读取消息数据(循环直到读取全部数据),然后将消息传递给处理。简单。
    • 你不需要担心"转义"分隔符,如果你希望它出现在一个正常的消息。

由于TCP是一个面向流的连接,如果写的速度比读的速度快,或者比TCP堆栈发送数据包的速度快,这种行为是正常的。

您应该添加一个分隔符来分隔流的各个部分,例如,通过对子数据包使用长度字段,或通过使用分隔符,如换行符(n, char code 10)。

另一种选择是使用UDP(甚至SCTP),但这取决于要完成的任务。

最新更新