recv()延迟问题

时间:2012-07-31 11:22:06

标签: c++ sockets recv

我已经多次询问过这个问题,但是没有一个解决方案似乎适用于我。在继续之前,我将为您发布一些代码:

    // Await the response and stream it to the buffer, with a physical limit of 1024 ASCII     characters
    stringstream input;
    char buffer[4096*2];
    while (recv(sock, buffer, sizeof(buffer) - 1, MSG_WAITALL) > 0)
        input << buffer;
    input << '\0';

    // Close the TCP connection
    close(sock);
    freehostent(hostInfo);

这是我的要求:

    string data;
    {
        stringstream bodyStream;

        bodyStream
            << "POST /api/translation/translate HTTP/1.1\n"
            << "Host: elfdict.com\n"
            << "Content-Type: application/x-www-form-urlencoded\n"
            << "Content-Length: " << (5 + m_word.length())
            << "\n\nterm=" << m_word;

        data = bodyStream.str();
    }

    cout << "Sending HTTP request: " << endl << data << endl;

我对这种编程非常陌生(并且堆栈溢出 - 更喜欢把它拖出来并且撞到墙上,直到我自己解决问题,但我迷失在这里!)并且非常感谢帮助找出原因这需要这么久!我已经考虑过设置它以便它是非阻塞的,但是有问题让它按预期工作。虽然也许这里的人可以指出我正确的方向,如果非bocking路线是我需要去的方式。

我看过很多人喜欢使用图书馆,但我想学会这样做!

我也是在mac上编程并使用套接字的新手。可能不是最好的第一次项目,但我现在就开始了!所以我希望继续:)任何帮助都会很好!

提前谢谢!

2 个答案:

答案 0 :(得分:2)

接收需要很长时间的原因是因为你告诉系统要等到你收到所有数据,即8k字节,或者连接上有错误或者它已关闭。这是标志MSG_WAITALL的作用。

对此的一个解决方案是使套接字无阻塞,并在循环中连续读取,直到我们收到错误或连接关闭。

如何使套接字非阻塞因平台而异,在Windows上使用ioctlsocket函数完成,在Linux或类似系统上使用fcntl函数完成:

int flags = fcntl(sock, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);

然后你就像这样读取套接字:

std::istringstream input;

for (;;)
{
    char buffer[256];
    ssize_t recvsize;

    recvsize = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (recvsize == -1)
    {
        if (errno != EAGAIN && errno != EWOULDBLOCK)
            break;  // An error
        else
            continue; // No more data at the moment
    }
    else if (recvsize == 0)
        break;  // Connection closed

    // Terminate buffer
    buffer[recvsize] = '\0';

    // Append to input
    input << buffer;
}

上述循环的问题在于,如果没有收到任何数据,它将永远循环。


但是,您的代码中存在更严重的问题:您收到一个缓冲区,然后将其附加到stringstream,但您没有终止缓冲区。您不需要在流中终止字符串,它会自动完成,但您执行需要终止缓冲区。

这可以这样解决:

int rc;
while ((rc = recv(sock, buffer, sizeof(buffer) - 1, MSG_WAITALL)) > 0)
{
    buffer[rc] = '\0';
    input << buffer;
}

答案 1 :(得分:1)

此处出现问题是因为您指定了MSG_WAITALL标志。它会强制recv保持阻塞状态,直到收到所有指定的字节(在您的情况下为sizeof(buffer) - 1,而另一方发送的消息明显较小)或发生错误并返回-1正确设置errno变量。

我认为,更优选的选择是在循环中没有任何标志的情况下导致recv,直到另一端的套接字关闭(recv返回0)或接收到某个分隔符。

但是,您应该小心使用input << buffer,因为recv在每次迭代时可能只返回一小部分数据(例如,20个字节),因此您应该准确地输入这些数据量字符串流。收到的字节数由recv返回。