我已经多次询问过这个问题,但是没有一个解决方案似乎适用于我。在继续之前,我将为您发布一些代码:
// 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上编程并使用套接字的新手。可能不是最好的第一次项目,但我现在就开始了!所以我希望继续:)任何帮助都会很好!
提前谢谢!
答案 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
返回。