4

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

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)),只返回它的一部分。写方总是向我发送整个数据(我用嗅探器得到它)。有什么关系?谢谢。

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

PS2我忘记了——它阻塞了套接字。

4

3 回答 3

11

TCP 标准允许对数据包进行分段。实际上,这不会发生在几百字节左右的小数据包中,但几乎可以肯定,一兆字节的数据会被分割。

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

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

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

于 2010-12-22T13:25:22.407 回答
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 循环中添加另一个终止条件。

于 2010-12-22T13:34:45.037 回答
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;
}
于 2010-12-22T13:46:26.497 回答