Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C++ Windows Networking
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:41am
Edited 24-Aug-12 8:09am
v2
Comments
JackDingler at 24-Aug-12 12:48pm
   
Is the webserver taking that long to respond?
 
Or do you know?
alleqs at 24-Aug-12 14:07pm
   
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 at 24-Aug-12 14:31pm
   
Recv is a blocking call.
 
Use select() to see if data is waiting, before making the call.
alleqs at 24-Aug-12 16:04pm
   
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 at 24-Aug-12 16:15pm
   
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 at 24-Aug-12 16:37pm
   
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 at 24-Aug-12 16:51pm
   
That was quick! You accepted the answer almost before I finished it. :-) So have you check the problem by logging?
alleqs at 24-Aug-12 16:55pm
   
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 at 24-Aug-12 16:53pm
   
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 at 24-Aug-12 16:56pm
   
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 at 24-Aug-12 17:05pm
   
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 at 24-Aug-12 17:15pm
   
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 at 24-Aug-12 17:16pm
   
I saw that. Good catch.

1 solution

Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

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  
v2
Comments
alleqs at 24-Aug-12 16:51pm
   
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 at 24-Aug-12 16:53pm
   
You are welcome!
JackDingler at 24-Aug-12 17:15pm
   
My 5
pasztorpisti at 24-Aug-12 17:18pm
   
Thank you!

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

  Print Answers RSS
0 Maciej Los 245
1 OriginalGriff 210
2 Manfred R. Bihy 190
3 CHill60 180
4 _Amy 155
0 OriginalGriff 7,445
1 Sergey Alexandrovich Kryukov 6,157
2 Maciej Los 3,774
3 Peter Leow 3,448
4 CHill60 2,702


Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 24 Aug 2012
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100