Java套接字问题:数据包在接收方合并

时间:2011-10-14 16:04:22

标签: java sockets client

我遇到套接字问题。当我在同一台PC上运行服务器和客户端时,即使用" localhost"参数。但是,当使用不同的PC时,看不到问题。 客户端使用以下代码发送文件:

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

然后在另一种方法中,我发送了一个命令:

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

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

    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;
        }
    }

3 个答案:

答案 0 :(得分:7)

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

在TCP中,您不能依赖单独接收的单独“数据包”(例如,发送4个10字节的块可以作为1个40块接收,或者2个块中的20个接收,或者一个39块接收1)的一大块。 TCP保证订单交付,但不保证数据的任何特定“打包”。

因此,例如,如果您要发送字符串,则需要先发送字符串长度,然后再发送字节。伪代码中的逻辑类似于:

<强>客户端:

  1. 发送命令指示器
  2. 发送有效负载长度
  3. 发送有效负载
  4. 服务器

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

答案 1 :(得分:5)

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

如果您需要将流分成单个消息,则应为每条消息使用分隔符或(最好是IMO)长度前缀。您还应该预期任何读取您的问题可能无法获得您所要求的数据 - 换句话说,如果您不小心,不仅可以合并消息,而且可以容易分裂。

我提到我更喜欢使用长度前缀来分隔符。利弊:

  • 使用邮件分隔符的好处是在开始发送之前不需要知道邮件大小。
  • 使用长度前缀的好处是:
    • 阅读消息的代码不需要关心消息中的数据 - 它只需要知道它有多长。您阅读了消息长度,您阅读了消息数据(循环播放直到您全部阅读完毕),然后您将消息传递给进程。简单。
    • 如果要将分隔符显示在正常消息中,则无需担心“转义”分隔符。

答案 2 :(得分:3)

由于TCP是面向流的连接,如果编写器写入速度比读取器读取速度快,或者TCP堆栈发送数据包,则此行为是正常的。

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

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