如何适应写入 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"。首先发送数据包的大小,然后发送数据包本身。然后接收方将知道他们何时获得所有数据。
发送方
- 发送数据包的长度(已知大小——比如 32 位整数)
- 发送数据包
接收方
- 读取数据包长度
- 读取那么多字节的数据
- 处理完全接收的数据包
查看这篇文章了解更多信息: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 和零,并采取适当的措施,每种情况下的措施都不同。
作为测试,我正在从 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"。首先发送数据包的大小,然后发送数据包本身。然后接收方将知道他们何时获得所有数据。
发送方
- 发送数据包的长度(已知大小——比如 32 位整数)
- 发送数据包
接收方
- 读取数据包长度
- 读取那么多字节的数据
- 处理完全接收的数据包
查看这篇文章了解更多信息: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 和零,并采取适当的措施,每种情况下的措施都不同。