如何适应写入 tcp 套接字的时间可变性?

How to accommodate timing variability in writing to tcp socket?

作为测试,我正在从 Android 应用程序向 TCP 套接字写入一系列字节数组,并在 C++ 应用程序中读取它们。

Java

InetAddress address = InetAddress.getByName("192.168.0.2");                          
Socket socket = new Socket(address, 1300);                   
DataOutputStream out = new DataOutputStream(socket.getOutputStream())

...

if(count == 0) {
    out.write(first, 0, first.length);
} else if(count == 1) {
    out.write(second, 0, second.length);
}

C++

do {
    iResult = recv(ClientSocket, recvbuf, 3, 0);
    for (int i = 0; i < 3; i++) {
        std::cout << (int)(signed char)recvbuf[i] << std::endl;
    }
} while (iResult > 0);

就目前而言,在第一张收据上,recv[2] = -52,我认为这是一个垃圾值,因为到我收到时输出流还没有写入第二个字节数组第一段.

但是,当我在 ListenSocket 接受连接后暂停时:

ClientSocket = accept(ListenSocket, NULL, NULL);
std::cin.ignore();

...让发送者有时间对流进行两次写入,recv[2] = 3,这是第二个写入字节数组的第一个值。

如果我最终想发送和接收一个恒定的离散数组流,我如何确定在我收到一个数组的最后一个值后,缓冲区中的下一个值是否是下一个数组的第一个值数组还是垃圾值?

我考虑过udp更适合发送一系列离散的数据集,但我需要tcp的可靠性。我想 tcp 经常以这种方式使用,但我不清楚如何缓解这个问题。

编辑: 在我编写此测试的实际应用程序中,我确实实现了长度前缀。不过我认为这无关紧要;即使我知道我在数据集的末尾,我也需要知道缓冲区中的下一个值是垃圾还是下一组的开始。

正如您所指出的,TCP 是基于流的,因此没有内置的方式来表达 "here's a specific chunk of data"。您要做的是添加您自己的 "message framing"。一个简单的方法叫做 "length prefixing"。首先发送数据包的大小,然后发送数据包本身。然后接收方将知道他们何时获得所有数据。

发送方

  1. 发送数据包的长度(已知大小——比如 32 位整数)
  2. 发送数据包

接收方

  1. 读取数据包长度
  2. 读取那么多字节的数据
  3. 处理完全接收的数据包

查看这篇文章了解更多信息:http://blog.stephencleary.com/2009/04/message-framing.html

for (int i = 0; i < 3; i++)

问题就在这里。应该是:

for (int i = 0; i < iResult; i++)

您正在打印您可能没有收到的数据。这是'junk value'.

的解释

您不能假设 recv() 会填满缓冲区。

在此循环之前,您还必须检查 iResult 的 -1 和零,并采取适当的措施,每种情况下的措施都不同。