Question

I'm trying to write a very simple HTTP server using Boost.Asio. Here is the code (almost the same as the example from Boost.Asio tutorial)

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <thread>
#include <chrono>

using boost::asio::ip::tcp;

int main()
{
    try
    {
        boost::asio::io_service io_service;

        tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 12345));

        for (;;)
        {
            tcp::socket socket(io_service);
            acceptor.accept(socket);

            const char message[] = "HTTP/1.0 200 OK\r\n\r\n<html><body><i>Hello, world</i></body></html>";

            boost::system::error_code ignored_error;
            boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
        }
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

When I run this sample, I try with Chrome at address 127.0.0.1:12345, but it shows "This webpage is not available". But if I start in a debugger and step by step, it correctly displays the italicized "Hello, world". In fact, it will work correctly if I add a line std::this_thread::sleep_for(std::chrono::seconds(1)); after the write operation. What am I doing wrong? Is there a way to avoid this ugly hack?

I'm using Visual Studio 2013 on Windows 7. Compiled as 64 bit code.

Was it helpful?

Solution

I think that the problem is an HTTP issue not a networking issue.

The message lacks a Content-Length header and so your HTTP/1.1 client (Chrome) is probably waiting for your server to close the connection to mark the end of the message body, see: rfc2616 section 4.4. Try changing the message to:

  "HTTP/1.0 200 OK\r\nContent-Length: 45\r\n\r\n<html><body><i>Hello, world</i></body></html>";

I hope I've got the Content-Length of your message body right. ;)

OTHER TIPS

Update

The idea that Content-Length maybe required by the client could have some merit.

Conversely, you could add a Connection: close header, which would prevent the browser from keeping it open (hence 'EOF' would count as end of response).


The code looks fine and it works without sleep on my box (Ubuntu/Opera).

The only things that strikes me as... interesting is that

  • you are sending the terminating NUL character as part of the response:

    $ netcat localhost 12345 | xxd
    0000000: 4854 5450 2f31 2e30 2032 3030 204f 4b0d  HTTP/1.0 200 OK.
    0000010: 0a0d 0a3c 6874 6d6c 3e3c 626f 6479 3e3c  ...<html><body><
    0000020: 693e 4865 6c6c 6f2c 2077 6f72 6c64 3c2f  i>Hello, world</
    0000030: 693e 3c2f 626f 6479 3e3c 2f68 746d 6c3e  i></body></html>
    0000040: 00                                       .
    
  • you are not receiving the request; depending on the client this might confuse the client a little (?). I don't think this can be the problem here because there's precious little request information to be split up into packages

Update Actually, the/a problem might occur if your browser does other requests (like GET /favicon.ico, e.g. in parallel. Your server handles connections synchronously and they might arrive simultaneously. Make sure that the listening socket allows for at least 1 (or maybe, say, 10) connections in the "backlog"

This old answer ('08) says backlog used to be 5 in Asio, by default.

I think the problem here is that you are not waiting for the request. If the socket is closed before the request is sent, the client will see this as a reset connection. Your one second delay trick works because it keeps the connection open for long enough for the client to send through its request.

Your example works without the delay trick if I add a dummy request listener, just before the write:

std::array<char, 8192> ignored_buffer;
socket.async_read_some(boost::asio::buffer(ignored_buffer),
    [](boost::system::error_code ec, std::size_t bytes_transferred) {});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top