Reputation: 13
This question has been asked a number of times, I have noted, but none of the solutions seem to be applicable to me. Before I continue I will post a little bit of code for you:
// 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);
And here is my request:
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;
I am very new to this sort of programming (and stack overflow- preferring to slog it out and bang my head against a wall until I solve problems myself but I'm lost here!) and would really appreciate help working out why it takes so long! I've looked into setting it up so that it is non-blocking, but had issues getting that to work as expected. Though maybe people here could point me in the right direction, if the non-bocking route is the way I need to go.
I have seen that a lot of people prefer to use libraries but I want to learn to do this!
I'm also new to programming on the mac and working with sockets. Probably not the best first time project maybe, but I've started now! So I wish to continue :) Any help would be nice!
Thank you in advance!
Upvotes: 1
Views: 2066
Reputation: 409196
The reason why it takes a long time to receive is because you tell the system to wait until it has received all data you ask for, i.e. 8k bytes, or there is an error on the connection or it is closed. This is what the flag MSG_WAITALL
does.
One solution to this is to make the socket non-blocking, and do a continuous read in a loop until we get an error or the connection is closed.
How to make a socket non-blocking differs depending on platform, on Windows it done with the ioctlsocket
function, on Linux or similar systems it is done with the fcntl
function:
int flags = fcntl(sock, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);
Then you read from the socket like this:
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;
}
The problem with the above loop is that if no data is ever received, it will loop forever.
However, you have a much more serious problem in your code: You receive into a buffer, and then you append it to the stringstream
, but you do not terminate the buffer. You do not need to terminate the string in the stream, it's done automatically, but you do need to terminate the buffer.
This can be solved like this:
int rc;
while ((rc = recv(sock, buffer, sizeof(buffer) - 1, MSG_WAITALL)) > 0)
{
buffer[rc] = '\0';
input << buffer;
}
Upvotes: 2
Reputation: 10541
The problem here happens because you are specifying MSG_WAITALL
flag. It forces the recv
to remain blocked until all the specified bytes are received (sizeof(buffer) - 1
in your case, while the message being sent by the other party is obviously smaller) or an error occurs and it returns -1 with errno
variable being set appropriately.
I think, a more preferable option would be to cause recv
without any flags in a loop until the socket on the other end is closed (recv returns 0) or some separator is received.
However, you should be careful using input << buffer
, because recv
might return only a small portion of data (for example, 20 bytes) on each iteration, so you should put exactly this amount of data to string stream. The number of bytes received is returned by recv
.
Upvotes: 1