简体   繁体   English

发出TCP套接字上的double的发送/接收向量(丢失数据)

[英]Issue sending/Receiving vector of double over TCP socket (missing data)

I am trying to send data from a vector over a TCP socket. 我正在尝试通过TCP套接字从向量发送数据。 I'm working with a vector that I fill with values from 0 to 4999, and then send it to the socket. 我正在使用向量填充0至4999的向量,然后将其发送到套接字。

Client side, I'm receiving the data into a vector, then I copy its data to another vector until I received all the data from the server. 客户端,我将数据接收到一个向量中,然后将其数据复制到另一个向量中,直到从服务器接收到所有数据为止。

The issue I'm facing is that when I receive my data, sometimes I will get all of it, and sometimes I will only receive the correct data from 0 to 1625 and then I get trash data until the end (please see the image below). 我面临的问题是,当我接收到数据时,有时会得到所有数据,有时我只会接收到0到1625之间的正确数据,然后我会得到垃圾数据直到结束(请参见下图) )。 I even received for example from 0 to 2600 correct data, then from 2601 to 3500 it's trash and finally from 3501 to 4999 it's correct again. 例如,我什至收到了0到2600的正确数据,然后从2601到3500收到了垃圾数据,最后从3501到4999再次得到了正确数据。

包含接收数据的文件
(left column is line number and right column is the data). (左列是行号,右列是数据)。

This is the server side : 这是服务器端:

vector<double> values2;
for(int i=0; i<5000; i++)
    values2.push_back(i);
skt.sendmsg(&values2[0], values2.size()*sizeof(double));

The function sendmsg : sendmsg函数:

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;

}

Client side : 客户端 :

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();


The outputs of the bytes are correct, the server outputs 40 000 when sending the data and the client also outputs 40 000 when receiving the data. 字节的输出正确,服务器在发送数据时输出40 000,客户端在接收数据时也输出40 000。

Removing the trailing zeros and then inserting the content of the buffer into a new vector is not very efficient, but I don't think it's the issue. 删除尾随的零,然后将缓冲区的内容插入到新的向量中不是很有效,但是我认为这不是问题。 If you have any clues on how to make it more efficient, it would be great! 如果您对如何提高效率有任何线索,那就太好了!

I don't really know if the issue is when I send the data or when I receive it, and also I don't really get why sometimes (rarely), I get all the data. 我真的不知道问题出在何时发送或何时接收数据,我也不清楚为什么有时(很少)获得所有数据。

recv receives bytes , and doesn't necessarily wait for all the data that was sent. recv接收字节 ,并且不一定等待所有已发送的数据。 So you can be receiving part of a double. 因此,您可以接收双打的一部分。

Your code works if you receive complete double values, but will fail when you receive part of a value. 如果您收到完整的double值,则您的代码将起作用,但是当您收到部分值时,代码将失败。 You should receive your data in a char buffer, then unpack it into doubles. 您应该在char缓冲区中接收数据,然后将其解压缩为双精度。 (Possibly converting endianness if the server and client are different.) (如果服务器和客户端不同,则可能转换字节顺序。)

#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);

This uses type punning from What's a proper way of type-punning a float to an int and vice-versa? 这使用类型修剪的方式是:将浮点型的类型修剪为int的正确方法,反之亦然? to convert the received binary data to a double, and assumes the endianness of the client and server are the same. 将接收到的二进制数据转换为double并假定客户端和服务器的字节序相同。

Incidentally, how does the receiver know how many values it is receiving? 顺便说一句,接收者如何知道它正在接收多少个值? You have a mix of hard coded values ( 5000 ) and dynamic values ( .size() ) in your server code. 服务器代码中混合了硬编码值( 5000 )和动态值( .size() )。

Note: code not compiled or tested 注意: 代码未经编译或测试

TL/DR: Never-ever send raw data via a network socket and expect them properly received/unpacked on other side. TL / DR:永远不要通过网络套接字发送原始数据,并期望它们在另一侧正确接收/解压缩。

Detailed answer: Network is built on top of various protocols, and this is for a reason. 详细答案:网络是建立在各种协议之上的,这是有原因的。 Once you send something, there is no warranty you counterparty is on the same OS and same software version. 一旦发送邮件,您的交易对手就不会在同一操作系统和同一软件版本上享有任何保证。 There is no standard how primitive types should be coded on byte level. 没有标准的字节类型应如何编码原始类型。 There is no restriction how much intermittent nodes could be involved into the data delivery, and each of your send() may traverse via different routes. 没有限制,可以在数据传递中涉及多少个间歇节点,并且每个send()都可以通过不同的路径遍历。 So, you have to formalize the way you send the data, then other party can be sure what is proper way to retrieve them from the socket. 因此,您必须规范发送数据的方式,然后另一方可以确定从套接字检索数据的正确方法是什么。

Simplest solution: use a header before your data. 最简单的解决方案:在数据前使用标头。 So, you plan to send 5000 doubles? 那么,您打算发送5000双打吗? Then send a DWORD first, which contains 40000 inside (5k elements, 8 bytes each -> 40k) and push all your 5k doubles right after that. 然后先发送一个DWORD,里面包含40000个(5k个元素,每个8个字节-> 40k),然后紧接着推送所有5k双打。 Then, your counterparty should read 4 bytes from the socket first, interpret it as DWORD and understand how much bytes should come then. 然后,您的交易对手应该首先从套接字读取4个字节,将其解释为DWORD,然后了解应该传入多少字节。

Next step: you may want to send not only doubles, but ints and strings as well. 下一步:您可能不仅要发送双精度数,还要发送整数和字符串。 That way, you have to expand your header so it can indicate 这样,您必须扩展标题,以便可以指示

  • Total size of further data (so called payload size) 其他数据的总大小(所谓的有效负载大小)
  • Kind of the data (array of doubles, string, single int etc) 数据种类(双精度数组,字符串,单整数等)

Advanced solution: Take a look on ready-to-go solutions: 先进的解决方案:看一下现成的解决方案:

Happy coding! 编码愉快!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM