recv有时不接收整个数据

时间:2010-12-22 13:19:29

标签: c++ linux network-programming recv

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

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;
};

但有时recv不返回整个数据(数据长度始终小于sizeof(temp)),只返回其中的一部分。写方面总是发给我整个数据(我用嗅探器得到它)。什么事? THX。

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

P.S.2 我忘记了 - 它阻塞了插座。

3 个答案:

答案 0 :(得分:11)

TCP标准允许数据包碎片化。实际上,对于几百字节左右的小数据包不会发生这种情况,但几兆字节的数据几乎肯定会被分割。

其次,当你说嗅探器说所有数据都被发送,在一个数据包或多个数据包中?

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

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

答案 1 :(得分:6)

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

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*/
    }
};

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

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

答案 2 :(得分:3)

TCP作为其他层之上的层工作:IP和以太网。 IP允许数据碎片,以太网允许一些数据通过线路丢失。这会导致数据丢失,并且会反映在您对recv。

的调用上

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

您需要创建自己的一些协议,以便继续阅读数据以完成数据。

例如,您可以使用“\ 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] = '\0';
        found = (strchr(temp, '\n') != 0);
    }

    inStr = temp;
}