Note that there are some explanatory texts on larger screens.

plurals
  1. POboost::asio read on socket to server blocks all incoming connection on client socket to be accepted
    text
    copied!<p>I'm quite new to boost::asio, currently writing http proxy (at the moment testing on Windows 7, boost 1.52.0 ). I faced behavior I don't really understand could you please help me with it.</p> <p>I'm using synchronios functions in current implementation. Here is my server class:</p> <pre><code>#include "server.h" namespace proxy { sync_accept_server::sync_accept_server(const std::string&amp; address, int port) : _io_service(), _acceptor(_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port )), _connection_id(0), _new_connection() { _acceptor.set_option(boost::asio::ip::tcp::no_delay(true)); } void sync_accept_server::start_accept() { boost::system::error_code error; // wait for incoming connections for (;;) { // create new connection //_new_connection.reset(new connection(_p_io_service_pool-&gt;get_io_service(), ++_connection_id)); _new_connection.reset(new sync_connection(++_connection_id)); std::cout &lt;&lt; "START WAITING FOR INCOMING CONNECTION" &lt;&lt; std::endl; _acceptor.accept(_new_connection-&gt;get_socket(), error); if ( !error ) { // create new thread boost::thread new_thread(boost::bind(&amp;sync_connection::start, _new_connection)); new_thread.detach(); //_new_connection-&gt;start(); std::cout &lt;&lt; " New connection was started, id: " &lt;&lt; _connection_id &lt;&lt; std::endl; } else { std::cout &lt;&lt; "Error during incoming connection acceptance: " &lt;&lt; error.message(); } } } } // namespace proxy </code></pre> <p>Here is connection class which reads headers from browser, setting up connection to server, sends data to server host, reads response and write that data back to browser</p> <pre><code>#include &lt;boost/assert.hpp&gt; #include &lt;boost/lexical_cast.hpp&gt; #include "sync_connection.h" namespace { std::string streambuf_to_string(boost::asio::streambuf &amp;buf) { std::ostringstream string_stream; string_stream &lt;&lt; &amp;buf; return std::string(string_stream.str()); } } // anonym namespace namespace proxy { sync_connection::sync_connection(int id) : _io_service(), _server_io_service(), _bsocket(_io_service), _bbuffer(), _ssocket(_server_io_service), _sbuffer(), _resolver(_server_io_service), _streambuf(), _headers(), _content_length(-1), _was_read(0), _id(id), _new_url(), END_SIGN("\r\n\r\n") { std::cout &lt;&lt; "Connection was created, id: " &lt;&lt; _id &lt;&lt; std::endl; } void sync_connection::start() { boost::system::error_code error; // read headers from browser socket // Note functoin could read more data but will return number of bytes up to and including END_SIGN std::size_t len = boost::asio::read_until(_bsocket, _streambuf, END_SIGN, error); BOOST_ASSERT(!error); // conver sream buf to string - so we could parse it _headers = streambuf_to_string(_streambuf); // parse headers _request.feed(_headers.c_str(), len); // start connect to server start_connect(); } void sync_connection::start_connect() { // parse URL http::Url url(_request.url()); std::string port = url.port().empty() ? "80" : url.port(); std::string server = url.host(); // Build new url _new_url = url.path(); std::string query = url.query(); if ( !query.empty() ) { _new_url += ( "?" + query ); } // host has a '/' sign at the begining of string it might cos some // connection troubles server = server.substr(1, server.npos); BOOST_ASSERT(!server.empty()); if ( server.empty() ) { return; } std::cout &lt;&lt; _id &lt;&lt; " Server: " &lt;&lt; server &lt;&lt; std::endl; boost::system::error_code error; // create query to be able to resolve hostname to ip and port boost::asio::ip::tcp::resolver::query resolver_query(server, port); // resolve to list of enpoints boost::asio::ip::tcp::resolver::iterator endpoint_iterator = _resolver.resolve(resolver_query, error); BOOST_ASSERT(!error); // till we are not connected ot out of enpoints bool is_connected = false; while ( endpoint_iterator != boost::asio::ip::tcp::resolver::iterator() &amp;&amp; !is_connected ) { // so let's try to connect here _ssocket.connect(*endpoint_iterator, error); //( error ) ? ( ++endpoint_iterator ) : ( is_connected = true ); if ( error ) { std::cout &lt;&lt; _id &lt;&lt; " Error occurs: " &lt;&lt; error.message() &lt;&lt; " try new endpoint" &lt;&lt; std::endl; ++endpoint_iterator; } else { is_connected = true; } } // raise exception in case we did not manage to connect to a server if ( !is_connected ) { std::cout &lt;&lt; _id &lt;&lt; " Did not manage to connect to a server, error: " &lt;&lt; error.message() &lt;&lt; std::endl; BOOST_ASSERT(false); } else { std::cout &lt;&lt; _id &lt;&lt; " We are connected now!" &lt;&lt; std::endl; _ssocket.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); start_write_to_server(); } } void sync_connection::start_write_to_server() { // prepare request which will be send to server std::string _proxy_request = _request.method_name(); _proxy_request += " "; _proxy_request += _new_url; _proxy_request += " HTTP/"; _proxy_request += "1.1"; // todo - we have to get protocol version from the requiest _proxy_request += "\r\n"; // end of request line // all other headers should be the same as in browser request _proxy_request += _headers; boost::system::error_code error; // so we can send data now boost::asio::write(_ssocket, boost::asio::buffer(_proxy_request), error); BOOST_ASSERT_MSG(!error, error.message().c_str()); _headers.clear(); // read response start_read_from_server(); } void sync_connection::start_read_from_server() { // so lets read only headers at the begining // clean streambuf since it could contain END_SIGN _streambuf.consume(_streambuf.size()); boost::system::error_code error; std::size_t len = boost::asio::read_until(_ssocket, _streambuf, END_SIGN, error); BOOST_ASSERT_MSG(!error, error.message().c_str()); // get headers _headers = streambuf_to_string(_streambuf); BOOST_ASSERT_MSG(!_headers.empty(), "Empty response from server, something went wrong"); // parse response from server _response.feed(_headers.c_str(), len); std::cout &lt;&lt; "HEADERS:\n" &lt;&lt; _headers &lt;&lt; std::endl; // check for Content-length header if ( _response.has_header("Content-Length") ) { std::cout &lt;&lt; _id &lt;&lt; " Content-Length is present" &lt;&lt; std::endl; _content_length = boost::lexical_cast&lt;int&gt;(_response.header("Content-Length")); } std::cout &lt;&lt; _id &lt;&lt; " CONTENT LENGTH " &lt;&lt; _response.has_header("Content-Length") &lt;&lt; std::endl; // since read_until could read more data then till END_SIGN // we need to calculate how many bytes of body was read // if there are bunch of data after END_SIGN if ( len &lt; _headers.size() ) { _was_read = _headers.size() - len; } start_write_to_browser(); } void sync_connection::start_write_to_browser() { // write response to browser boost::system::error_code error_server; boost::system::error_code error_browser; boost::asio::write(_bsocket, boost::asio::buffer(_headers), error_browser); BOOST_ASSERT_MSG(!error_browser, error_browser.message().c_str()); // do we need to read server body ? std::cout &lt;&lt; _id &lt;&lt; " Content-lenght: " &lt;&lt; _content_length &lt;&lt; std::endl; std::size_t len = 0; if ( _content_length == -1 || _was_read &lt; _content_length ) { bool done = false; do { std::cout &lt;&lt; _id &lt;&lt; " Start reading" &lt;&lt; std::endl; len = _ssocket.read_some(boost::asio::buffer(_sbuffer), error_server); _was_read += len; std::cout &lt;&lt; _id &lt;&lt; " Bytes was read: " &lt;&lt; _was_read &lt;&lt; std::endl; boost::asio::write(_bsocket, boost::asio::buffer(_sbuffer, len), error_browser); BOOST_ASSERT_MSG( !error_browser, error_browser.message().c_str() ); if ( error_server ) { std::cout &lt;&lt; _id &lt;&lt; "Error, stop reading: " &lt;&lt; error_server.message() &lt;&lt; std::endl; break; } std::cout &lt;&lt; _id &lt;&lt; " Data were written to Browser" &lt;&lt; std::endl; } while ( _content_length == -1 || _was_read &lt; _content_length ); } std::cout &lt;&lt; _id &lt;&lt; " we are done with this connection, close sockets" &lt;&lt; std::endl; shutdown(); } void sync_connection::shutdown() { _ssocket.close(); _bsocket.close(); } } // namespace proxy </code></pre> <p>The problem is when Content-Length is not present in Server response ( chunked transfer encoding ) - so in that case I don't know how many data should be read from browser, and call read_some method when there is no more data on socket. So call hangs for ~10-15 sec till connection will be closed by server (EOF). In general I'm fine with this behavior. </p> <p>But the problem is that this last read_some call on server socket blocks all new incoming connection from browser to be accepted by acceptor. New connections will not be accepted till read_some on server socket return EOF. </p> <p>I've done a lot of experements already, and interesting thing is that the same blocking call on browser socket will not block other incoming connections to be accepted. if I will call </p> <p>_bsocket.read_some(...)</p> <p>and there is no data to read and server keeping connection - then all new incoming connections accepted just fine.</p> <p>I really can't understand why does it work in that way, I don't have any relations between server and browser socket in my implementation, they even initialized with different io_service objects.</p> <p>Is there anything wrong in my implementation? I'm facing exactly the same issue with async functions. Any tips would be helpful. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload