Issue sending/Receiving double over TCP 套接字向量(丢失数据)
Issue sending/Receiving vector of double over TCP socket (missing data)
我正在尝试通过 TCP 套接字从矢量发送数据。
我正在使用一个向量,我用 0 到 4999 之间的值填充,然后将它发送到套接字。
客户端,我正在将数据接收到一个向量中,然后我将其数据复制到另一个向量中,直到我从服务器接收到所有数据。
我面临的问题是,当我收到我的数据时,有时我会收到所有数据,有时我只会收到从 0 到 1625 的正确数据,然后我收到垃圾数据,直到最后 (请参见下图)。例如,我什至收到了从 0 到 2600 的正确数据,然后从 2601 到 3500 是垃圾,最后从 3501 到 4999 又是正确的。
(左列是行号,右列是数据)。
这是服务器端:
vector<double> values2;
for(int i=0; i<5000; i++)
values2.push_back(i);
skt.sendmsg(&values2[0], values2.size()*sizeof(double));
发送消息函数:
void Socket::sendmsg(const void *buf, size_t len){
int bytes=-1;
bytes = send(m_csock, buf, len, MSG_CONFIRM);
cout << "Bytes sent: " << bytes << endl;
}
客户端:
vector<double> final;
vector<double> msgrcvd(4096);
do{
bytes += recv(sock, &msgrcvd[0], msgrcvd.size()*sizeof(double), 0);
cout << "Bytes received: " << bytes << endl;
//Get rid of the trailing zeros
while(!msgrcvd.empty() && msgrcvd[msgrcvd.size() - 1] == 0){
msgrcvd.pop_back();
}
//Insert buffer content into final vector
final.insert(final.end(), msgrcvd.begin(), msgrcvd.end());
}while(bytes < sizeof(double)*5000);
//Write the received data in a txt file
for(int i=0; i<final.size(); i++)
myfile << final[i] << endl;
myfile.close();
字节输出正确,服务端发送数据时输出40000,客户端接收数据时也输出40000。
删除尾随零然后将缓冲区的内容插入到新向量中效率不是很高,但我认为这不是问题所在。如果您对如何提高效率有任何线索,那就太好了!
我真的不知道问题是在我发送数据时还是在我接收数据时,我也不明白为什么有时(很少)我会收到所有数据。
recv
接收 字节 ,不一定等待发送的所有数据。所以你可以得到双倍的一部分。
如果您收到完整的 double
值,您的代码可以正常工作,但如果您收到部分值,则会失败。您应该在 char
缓冲区中接收数据,然后将其解压缩为双打。 (如果服务器和客户端不同,可能会转换字节顺序。)
#include <cstring> // For memcpy
std::array<char, 1024> msgbuf;
double d;
char data[sizeof(double)];
int carryover = 0;
do {
int b = recv(sock, &msgbuf[carryover], msgbuf.size() * sizeof(msgbuf[0]) - carryover, 0);
bytes += b;
b += carryover;
const char *mp = &msgbuf[0];
while (b >= sizeof(double)) {
char *bp = data;
for (int i = 0; i < sizeof(double); ++i) {
*bp++ = *mp++;
}
std::memcpy(&d, data, sizeof(double));
final.push_back(d);
b -= sizeof(double);
}
carryover = b % sizeof(double);
// Take care of the extra bytes. Copy them down to the start of the buffer
for (int j = 0; j < carryover; ++j) {
msgbuf[j] = *mp++;
}
} while (bytes < sizeof(double) * 5000);
这使用 What's a proper way of type-punning a float to an int and vice-versa? 中的类型双关将接收到的二进制数据转换为双精度数据,并假定客户端和服务器的字节顺序相同。
顺便说一句,接收方如何知道它接收了多少个值?您的服务器代码中混合了硬编码值 (5000
) 和动态值 (.size()
)。
注:代码未编译或测试
TL/DR:
Never-ever 通过网络套接字发送原始数据并期望它们在另一端正确 received/unpacked。
详细答案:
网络建立在各种协议之上,这是有原因的。一旦你发送了一些东西,就不能保证你的交易对手使用相同的 OS 和相同的软件版本。没有关于如何在字节级别上对原始类型进行编码的标准。没有限制有多少间歇性节点可以参与数据传输,并且您的每个 send() 都可以通过不同的路由遍历。因此,您必须规范发送数据的方式,然后其他方才能确定从套接字中检索数据的正确方法。
最简单的解决方案: 在数据前使用 header。那么,您打算发送 5000 个双打?然后先发送一个 DWORD,其中包含 40000 个内部(5k 个元素,每个 8 个字节 -> 40k),然后立即推送所有 5k 个双精度值。然后,你的对方应该首先从套接字读取 4 个字节,将其解释为 DWORD,然后了解应该有多少字节。
下一步:您可能不仅要发送双精度数,还要发送整数和字符串。这样,您必须扩展 header 才能指示
- 进一步数据的总大小(所谓的有效载荷大小)
- 数据类型(双精度数组、字符串、单个整数等)
高级解决方案:
查看 ready-to-go 解决方案:
- ProtoBuf https://developers.google.com/protocol-buffers/docs/cpptutorial
- Boost.Serialization https://www.boost.org/doc/libs/1_67_0/libs/serialization/doc/index.html
- Apache 节俭 https://thrift.apache.org
- YAShttps://github.com/niXman/yas
编码愉快!
我正在尝试通过 TCP 套接字从矢量发送数据。 我正在使用一个向量,我用 0 到 4999 之间的值填充,然后将它发送到套接字。
客户端,我正在将数据接收到一个向量中,然后我将其数据复制到另一个向量中,直到我从服务器接收到所有数据。
我面临的问题是,当我收到我的数据时,有时我会收到所有数据,有时我只会收到从 0 到 1625 的正确数据,然后我收到垃圾数据,直到最后 (请参见下图)。例如,我什至收到了从 0 到 2600 的正确数据,然后从 2601 到 3500 是垃圾,最后从 3501 到 4999 又是正确的。
(左列是行号,右列是数据)。
这是服务器端:
vector<double> values2;
for(int i=0; i<5000; i++)
values2.push_back(i);
skt.sendmsg(&values2[0], values2.size()*sizeof(double));
发送消息函数:
void Socket::sendmsg(const void *buf, size_t len){
int bytes=-1;
bytes = send(m_csock, buf, len, MSG_CONFIRM);
cout << "Bytes sent: " << bytes << endl;
}
客户端:
vector<double> final;
vector<double> msgrcvd(4096);
do{
bytes += recv(sock, &msgrcvd[0], msgrcvd.size()*sizeof(double), 0);
cout << "Bytes received: " << bytes << endl;
//Get rid of the trailing zeros
while(!msgrcvd.empty() && msgrcvd[msgrcvd.size() - 1] == 0){
msgrcvd.pop_back();
}
//Insert buffer content into final vector
final.insert(final.end(), msgrcvd.begin(), msgrcvd.end());
}while(bytes < sizeof(double)*5000);
//Write the received data in a txt file
for(int i=0; i<final.size(); i++)
myfile << final[i] << endl;
myfile.close();
字节输出正确,服务端发送数据时输出40000,客户端接收数据时也输出40000。
删除尾随零然后将缓冲区的内容插入到新向量中效率不是很高,但我认为这不是问题所在。如果您对如何提高效率有任何线索,那就太好了!
我真的不知道问题是在我发送数据时还是在我接收数据时,我也不明白为什么有时(很少)我会收到所有数据。
recv
接收 字节 ,不一定等待发送的所有数据。所以你可以得到双倍的一部分。
如果您收到完整的 double
值,您的代码可以正常工作,但如果您收到部分值,则会失败。您应该在 char
缓冲区中接收数据,然后将其解压缩为双打。 (如果服务器和客户端不同,可能会转换字节顺序。)
#include <cstring> // For memcpy
std::array<char, 1024> msgbuf;
double d;
char data[sizeof(double)];
int carryover = 0;
do {
int b = recv(sock, &msgbuf[carryover], msgbuf.size() * sizeof(msgbuf[0]) - carryover, 0);
bytes += b;
b += carryover;
const char *mp = &msgbuf[0];
while (b >= sizeof(double)) {
char *bp = data;
for (int i = 0; i < sizeof(double); ++i) {
*bp++ = *mp++;
}
std::memcpy(&d, data, sizeof(double));
final.push_back(d);
b -= sizeof(double);
}
carryover = b % sizeof(double);
// Take care of the extra bytes. Copy them down to the start of the buffer
for (int j = 0; j < carryover; ++j) {
msgbuf[j] = *mp++;
}
} while (bytes < sizeof(double) * 5000);
这使用 What's a proper way of type-punning a float to an int and vice-versa? 中的类型双关将接收到的二进制数据转换为双精度数据,并假定客户端和服务器的字节顺序相同。
顺便说一句,接收方如何知道它接收了多少个值?您的服务器代码中混合了硬编码值 (5000
) 和动态值 (.size()
)。
注:代码未编译或测试
TL/DR: Never-ever 通过网络套接字发送原始数据并期望它们在另一端正确 received/unpacked。
详细答案: 网络建立在各种协议之上,这是有原因的。一旦你发送了一些东西,就不能保证你的交易对手使用相同的 OS 和相同的软件版本。没有关于如何在字节级别上对原始类型进行编码的标准。没有限制有多少间歇性节点可以参与数据传输,并且您的每个 send() 都可以通过不同的路由遍历。因此,您必须规范发送数据的方式,然后其他方才能确定从套接字中检索数据的正确方法。
最简单的解决方案: 在数据前使用 header。那么,您打算发送 5000 个双打?然后先发送一个 DWORD,其中包含 40000 个内部(5k 个元素,每个 8 个字节 -> 40k),然后立即推送所有 5k 个双精度值。然后,你的对方应该首先从套接字读取 4 个字节,将其解释为 DWORD,然后了解应该有多少字节。
下一步:您可能不仅要发送双精度数,还要发送整数和字符串。这样,您必须扩展 header 才能指示
- 进一步数据的总大小(所谓的有效载荷大小)
- 数据类型(双精度数组、字符串、单个整数等)
高级解决方案: 查看 ready-to-go 解决方案:
- ProtoBuf https://developers.google.com/protocol-buffers/docs/cpptutorial
- Boost.Serialization https://www.boost.org/doc/libs/1_67_0/libs/serialization/doc/index.html
- Apache 节俭 https://thrift.apache.org
- YAShttps://github.com/niXman/yas
编码愉快!