Linux recv 有时接收不完整的数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4509570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 00:48:44  来源:igfitidea点击:

recv receiving not whole data sometime

c++linuxnetwork-programmingrecv

提问by milo

I have following issue: here is the chunk of code:

我有以下问题:这是代码块:

void get_all_buf(int sock, std::string & inStr) {
    int n = 1;
    char c;
    char temp[1024*1024]; 

    bzero(temp, sizeof(temp));

    n = recv(sock, temp, sizeof(temp), 0);

    inStr = temp;
};

but sometimes recvreturning not whole data (data length always less then sizeof(temp)), only its part. Write side always sends me wholedata (I got it with sniffer). What matter? Thx.

但有时recv不返回全部数据(数据长度总是小于sizeof(temp)),只返回它的一部分。写端总是向我发送整个数据(我用嗅探器得到了它)。什么事?谢谢。

P.S.I know, good manner suggests me to check n(if (n < 0) perror ("error while receiving data")), but it doesn't matter now - it's not reason of my problem.

PS我知道,礼貌建议我检查n( if (n < 0) perror ("error while receiving data")),但现在没关系 - 这不是我的问题的原因。

P.S.2I've forgot - it's blocking socket.

PS2我忘了 - 它阻塞了套接字。

采纳答案by AlastairG

The TCP standard allows for fragmentation of data packets. In practice this doesn't happen with small data packets of a few hundred bytes or so, but a megabyte of data is almost certain to get fragmented.

TCP 标准允许对数据包进行分段。实际上,这种情况不会发生在几百字节左右的小数据包中,但几乎肯定会出现一兆字节的数据碎片。

Secondly, when you say the sniffer says all the data gets sent, in one packet or in many?

其次,当你说嗅探器说所有数据都被发送时,一个数据包还是多个数据包?

Good network programming practice requires you to not assume that messages arrive in singular chunks. Two sequential messages can arrive as one packet (in theory but almost never in practice) and even if they arrive in multiple packets can be read as a single read. One message can get fragmented into multiple packets and they might not all arrive at once which is probably what you are seeing.

良好的网络编程实践要求您不要假设消息以单个块的形式到达。两个连续的消息可以作为一个数据包到达(理论上但在实践中几乎从来没有),即使它们以多个数据包的形式到达也可以作为单次读取读取。一条消息可以分成多个数据包,它们可能不会同时到达,这可能是您所看到的。

Your program should buffer all its reads and have a mechanism to determine when a whole message has arrived, either via a delimiter (e.g. HTTP headers which are delimited with CRLFCRLF) or by a byte count (e.g. HTTP bodies where the length is specified in the header) or by closing the connection to indicate the end of the data (e.g. HTTP bodies when the content length isn't specified in the header). There may be other mechanisms too.

您的程序应该缓冲所有读取,并有一种机制来确定整个消息何时到达,通过分隔符(例如,用 CRLFCRLF 分隔的 HTTP 标头)或字节计数(例如,长度在标头)或通过关闭连接来指示数据的结尾(例如,当标头中未指定内容长度时的 HTTP 正文)。可能还有其他机制。

回答by Juraj Blaho

A much better way is to use following:

更好的方法是使用以下方法:

void get_all_buf(int sock, std::string & output) {
    char buffer[1024];

    int n;
    while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || 
          errno == EINTR)
    {
        if(n>0)
            output.append(buffer, n);
    } 

    if(n < 0){
        /* handle error - for example throw an exception*/
    }
};

Also note that the buffer allocated on the stack is much smaller. Having 1M buffer on stack may cause stack overflow.

另请注意,堆栈上分配的缓冲区要小得多。堆栈上有 1M 缓冲区可能会导致堆栈溢出。

Additional note: You probably don't want to read until the socket is closed, so you may need to add another termination condition to the while loop.

附加说明:您可能不想在套接字关闭之前读取,因此您可能需要向 while 循环添加另一个终止条件。

回答by vz0

TCP works as a layer on top of other layers: IP and Ethernet. IP allows data fragmentation, and Ethernet allows some data to get lost over the wire. That leads to data loss, and it's reflected on your calls to recv.

TCP 作为其他层之上的一层:IP 和以太网。IP 允许数据分段,而以太网允许某些数据通过线路丢失。这会导致数据丢失,这会反映在您对 recv 的调用上。

When you call recv, the underlaying operating system will try to read as much data as it can up to the size you specified, but might return the call having read less bytes, even one single byte.

当您调用 时recv,底层操作系统将尝试读取尽可能多的数据,直至达到您指定的大小,但可能会返回读取较少字节的调用,甚至是单个字节。

You need to create some protocol of your own to keep reading data up to finishing your data piece.

您需要创建自己的一些协议,以继续读取数据直到完成您的数据片段。

For example, you can use "\n" as a delimiter. This code can be improved, but I hope will get you the idea:

例如,您可以使用“\n”作为分隔符。这段代码可以改进,但我希望能让你明白:

void get_all_buf(int sock, std::string & inStr) {
    int n = 1, total = 0, found = 0;
    char c;
    char temp[1024*1024]; 

    // Keep reading up to a '\n'

    while (!found) {
        n = recv(sock, &temp[total], sizeof(temp) - total - 1, 0);
        if (n == -1) {
            /* Error, check 'errno' for more details */
            break;
        }
        total += n;
        temp[total] = '##代码##';
        found = (strchr(temp, '\n') != 0);
    }

    inStr = temp;
}