Click here to Skip to main content
Sign Up to vote bad
good
Hello,
 
I'm developing a simple HTTP client in C++ (using VS2010) and I'm using an apache located at localhost for testing.
 
While the send'ing code workes like a charm, the recv takes up to 6-7 seconds to receive the whole message (500 bytes or so).
 
The actual question: how come is recv so slow? (I'm also setting TCP_NODELAY on my socket)
 
My implementation of the receiving routine:
int HttpClient::_receive()
{
    char buffer[1024];
    int  iReceivedBytes = 0;
 
    // init the buffer to an empty string
    buffer[0] = 0;
 
    while(1)
    {
        iReceivedBytes = recv(m_socket, buffer, 1024, 0);
        if (iReceivedBytes > 0)
        {
            // m_response is a member std::string
            m_response.append(buffer, iReceivedBytes);
        } else
           break;
    }
 
    // parse message
    return _parseMessage();
}
 
Edit: When testing the apache with a browser it takes less than 1 second to perform the same request.
 
Thanks,
Alex
Posted 24 Aug '12 - 6:41
Edited 24 Aug '12 - 8:09

Comments
JackDingler - 24 Aug '12 - 12:48
Is the webserver taking that long to respond? Or do you know?
alleqs - 24 Aug '12 - 14:07
No, the webserver is not the culprit in this case. It takes my browser less than 1 sec to load the same message (a short .htm file). Also it's noticeable when debugging as I step through code that recv blocks.
JackDingler - 24 Aug '12 - 14:31
Recv is a blocking call. Use select() to see if data is waiting, before making the call.
alleqs - 24 Aug '12 - 16:04
While it is my intention for recv to block (is running in a separate thread), the actual problem is that it blocks for too long. I updated the code to use the select function and still nothing. Also, when I test the code on a remote server (google.com) the recv function blocks until I stop debugging. Could it have anything to do with the socket creation code? Btw, thanks for your input.
JackDingler - 24 Aug '12 - 16:15
Oh... that explains a bit... The debugger blocks/slows threads while you're stepping through code. It can be a bit tricky to debug multi-threaded applications. Try using TRACE statements to debug around the recv call rather than stepping in with the debugger. then the code can run at full speed and you'll see the reports in the output window.
alleqs - 24 Aug '12 - 16:37
You are right about multi-threading debug. But somehow my case does not apply. This class is consumed by a worker thread. The thread itself is controlled by the UI using WINAPI message queues (PostMessage). The time it takes to receive the response is exactly the same when running in debug/release. Also, I set 2 breakpoints: one at the beginning of the method (the one shown above), and one at the end; and the time it takes the code to reach from A-B it's the same it takes when running without debugger. I'm stuck with this problem for a few days now, can't seem to find any clue. :(
pasztorpisti - 24 Aug '12 - 16:51
That was quick! You accepted the answer almost before I finished it. :-) So have you check the problem by logging?
alleqs - 24 Aug '12 - 16:55
Nope I seen the results in the UI. My worker thread sends a notification to the main window which itself updates an multiline edit control with the response. Actually google.com is faster than locahost apache, LOL.
JackDingler - 24 Aug '12 - 16:53
Can you verify that the send() puts the packet on the wire in a timely matter? The send() call is non blocking. The packet may be queued for delivery, but not instantly sent.
pasztorpisti - 24 Aug '12 - 16:56
Nagle works with fractions of a second, it should never ever cause 5 seconds delay. send() blocks if the socket isnt put into nonblocking mode and the send buffer of the socket doesnt have enough free space to store the data passed in by send().
JackDingler - 24 Aug '12 - 17:05
Fractions of a second should be longer than it takes to return from a function call... My point is that the send() call doesn't wait for the packet to leave the network card, or acknowledged at the other end of the connection. My main reason to post that, was to make sure that we've actually identified where the problem is. As you point out, there shouldn't be a five second delay on send(), but there shouldn't be that delay on recv() in this case either.
alleqs - 24 Aug '12 - 17:15
There is absolutely no concern with the send(). The reason why recv was blocking in the last call was because I wasn't specifing "Connection: close" in the HTTP header, and the server was keeping the connection alive thus causing recv to block. Now the requests take less than a sec on google.com (URL parsing, hostname resolving included).
JackDingler - 24 Aug '12 - 17:16
I saw that. Good catch.

1 solution

TCP_NODELAY has effect only on sending, it doesn do anything with recv(). It turns on/off the nagle alorithm that makes bandwith usage better if you call send() on a socket often with small data sizes.
 
Here is my guess: You exit your infinite loop when recv() returns zero or negative. This is bad for two reasons.
A negative values means that something very bad happened and you should handle it specially. Zero is returned by recv() when the connection has been shut down on the other side - for example because the http server has closed its socket. Your problem is probably that you are waiting for this socket close event and you should not rely on it. If you are using HTTP 1.0 header in your request then your http session isnt using permanent connection, in case of HTTP 1.1 its the same only if you use the Connection: close[^] header in your request. But even if the connection of http session isn't permanent noone guarantees for you that the server closes the connection after sending the data. To check if your problem is what I described do the following: After every recv() log out the totoal number of bytes received. Also print a log message when you break out from your loop. I guess you receive all data in less than a second, but the break executes only after 5 seconds when the server closes the connection.
 
To avoid the previous issues your http client should work like this: Currently you read the whole packet including the header and the payload and you interpret it just after this. Instead this you should read only the header part first - its easy to detect it, a double newline (CRLF) - and then you should parse/interpret the header. If the response doesnt use chunked transfer encoding (indicated by the "Transfer-Encoding: chunked" header in the response) then the payload of the HTTP response will have fixed size indicated by the "Content-Length" header field. I guess you dont want to handle chunked transfer encoding so you will look for Content-Length headers instead. So you read the header and found out the size of the payload. After this you know how many bytes following the header - you just read that out from the socket and the close the connection.
  Permalink  
Comments
alleqs - 24 Aug '12 - 16:51
The "Connection: Close" entry did the job. The server was keeping the connection alive. The code posted above is a bad example of coding but this was not the purpose of the question. Thank you very much!
pasztorpisti - 24 Aug '12 - 16:53
You are welcome!
JackDingler - 24 Aug '12 - 17:15
My 5
pasztorpisti - 24 Aug '12 - 17:18
Thank you!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Your Filters
Interested
Ignored
     
0 Sergey Alexandrovich Kryukov 464
1 Mahesh Bailwal 373
2 Maciej Los 215
3 CPallini 175
4 Aarti Meswania 173
0 Sergey Alexandrovich Kryukov 9,417
1 OriginalGriff 7,204
2 CPallini 3,933
3 Rohan Leuva 3,211
4 Maciej Los 2,743


Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 24 Aug 2012
Copyright © CodeProject, 1999-2013
All Rights Reserved. Terms of Use
Layout: fixed | fluid