read: End of file in ASIO. Why is socket getting closed?
Asked Answered
O

3

8

I have the example(*) code like this:

std::string protocol = "http";
std::string server = "www.example.com";
std::string path = "/";

asio::io_service service;

asio::ip::tcp::resolver resolver(service);
asio::ip::tcp::resolver::query query(server, protocol);

asio::ip::tcp::socket socket(service);

asio::ip::tcp::resolver::iterator end;
auto it = resolver.resolve(query);
asio::error_code error;
socket.connect(*it, error);
while(error && ++it!=end)
{
    socket.close();
    socket.connect(*it, error);
}

asio::streambuf request;
std::ostream request_stream(&request);

request_stream << "GET " << path << " HTTP/1.0\r\n";
request_stream << "Host: " << server << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";

asio::streambuf response;
size_t s = asio::read(socket, response);

When asio::read is called i get:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::system_error> >'
  what():  read: End of file

As i understand it, this error happens because the socket is getting closed while trying to read. But i do not understand why is it getting closed.

I have originally had read_until there, and it had the same error.

Am i doing something wrong with initiation of the connection or is it getting terminated for some other reason?

(*) Example means, that it is an example, and not a complete working program. If the example can be made shorter or better, please feel free to edit the question.


Per sehe's answer i have attempted the following:

size_t s = asio::read(socket, response, error);

if(!error || error==asio::error::eof)
{
    if(s==0)
    {
        std::cerr << "Same problem" << std::endl;
    }
}

The result is that "Same problem" is displayed when running the program, which means that it gets EOF before reading any data.


Per YSC's answer i have tried the following:

for (;;)
{
    char buf[255];

    size_t len = socket.read_some(asio::buffer(buf, 255), error);

    if (error == asio::error::eof)
    {
        std::cout << std::endl << "Connection closed" << std::endl;
        break; // Connection closed cleanly by peer.
    }
    else if (error)
    {
        std::cerr << "Error" << std::endl;
        return 1;
    }

    std::cout.write(buf, len);
}

No data appears, and "Connection closed" is shown after a slight delay. Which means that i get EOF without any data with that approach as well.

Oxeyed answered 21/11, 2015 at 9:28 Comment(2)
Note that the throw most certainly happens inside the read(), not before or after. Which means you would have to surround that statement with a try/catch. However, from my experience, ASIO is not done for the type of access you are trying to do there. I may be wrong, but it is well adapted for things using more permanent connections rather than a one query and one answer. With a NONBLOCK socket, you cannot be sure that the client will read everything written by the server. That's because the client may shutdown the socket and that flushes any data left over!Tude
I am not seeing where you write out your request data.Electroballistics
I
7

Just handle the error condition: http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/reference/read/overload2.html

asio::streambuf response;
boost::system::error_code ec;
size_t s = asio::read(socket, response, ec); // no condition, so read till EOF

if (!ec || ec == boost::asio::error::eof) {
    // woot, no problem
}
Insecure answered 21/11, 2015 at 15:57 Comment(4)
Hi. Thank you, but, as i've said before, my actual code is in fact different. And the question is not "how do i deal with EOF", but "why am i getting EOF, rather than data". I will try your approach (haven't yet), but i suspect i will just have 0 written in s, and i will end up inside if with no data and // woot, same problem.Oxeyed
Yep. Exactly the same thing. I get EOF with no data.Oxeyed
The actual code where the problem appears is shown above. Your code has the exact same problem.Oxeyed
I'll look at it later. (I think you mean you have the same problem when using my code snippet)Insecure
J
0

Your socket has a read_some() method which does not throw upon closing, but returns boost::asio::error::eof.

You might find this boost tutorial useful.

Julianejuliann answered 21/11, 2015 at 10:43 Comment(4)
I'm sorry, how does this answer the question? It's like doctor saying "You are ill".Oxeyed
You have two problems here: the first one is your socket closing, the second is your program crashing in this case. My answer help you fix the second. Do you want a program that just crashes when the server he tries to connect to closes the connection? But if you're not happy let me delete my answer.Julianejuliann
I have provided small example to show the problem. If a different code shows this problem better, please feel free to edit my question.Oxeyed
Yes, you are ill (that's useful to know) and it's normal (that's useful to know). Honestly, read the answer and chew on it before rejecting it. @Julianejuliann a link only answer is rarely helpfulInsecure
W
0

Sample code that might help (yes late answer). The problem is not that socket is closed. Error "end of file" is generated trying to read more bytes than exists in response.

Create endpoint from host name, and send request to that endpont and wait for response data to be read.

/** ---------------------------------------------------------------------------
 * @brief Try to get endpoint (where to send information) from host name
 * @param stringHostName 
 * @return true if host name is resolved, false and error information if error
*/
std::pair<bool, std::string> AsioResolve( const std::string_view& stringHostName, boost::asio::ip::tcp::endpoint& endpoint )
{
    boost::system::error_code errorcode;
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::resolver::query query( stringHostName.data(), "80");

    auto result_ = resolver.resolve( query, errorcode );
    if( errorcode.failed() == true ) return { false, errorcode.what() };

    if( result_.empty() == false )
    {
        endpoint = std::begin( result_ )->endpoint();                                    // get first endpoint from resolved address
        return { true, "" };
    }

    std::string stringError("failed to find ip for: ");
    stringError += stringHostName;
    return { false, stringError };
}

/** ---------------------------------------------------------------------------
 * @brief Send rest command to connected socket
 * @param socketSend socket used to send command
 * @param stringCommand command to send
 * @return true and response text on success, false and error information if error
*/
std::pair<bool, std::string> AsioSend( boost::asio::ip::tcp::socket& socketSend, const std::string_view& stringCommand )
{
    boost::system::error_code errorcode; // error code if error

    boost::asio::write( socketSend, boost::asio::buffer(stringCommand), boost::asio::transfer_all(), errorcode );
    if( errorcode.failed() == true ) return { false, errorcode.what() };

    // ## get response size, wait until response size is read
    unsigned uCountRead = 0;
    size_t uAvailableDataSize = socketSend.available();
   while( uAvailableDataSize == 0 ) 
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        uAvailableDataSize = socketSend.available();
        uCountRead++;
   }

    boost::asio::streambuf streambufResponse;
    size_t uSize;
    do
    {
        uSize = boost::asio::read(socketSend, streambufResponse, boost::asio::transfer_at_least(1), errorcode);

        uAvailableDataSize -= uSize;
    }
    while( errorcode.failed() == false && uAvailableDataSize > 0 );
    if( errorcode.failed() == true ) return { false, errorcode.what() };

    std::string stringResponse = boost::asio::buffer_cast<const char*>(streambufResponse.data());

    return { true, stringResponse };
}

TEST_CASE( "[http] 01", "[http]" ) {

    boost::asio::ip::tcp::endpoint endpointExample;
    auto result_ = AsioResolve( "example.com", endpointExample );
    if( result_.first == false ) { std::cout << result_.second; }                                               REQUIRE( result_.first == true );

    boost::system::error_code errorcode;
    boost::asio::io_context iocontextExample;   // core io functionality
    boost::asio::ip::tcp::socket socketExample( iocontextExample );
    socketExample.connect( endpointExample, errorcode );
    if( errorcode.failed() == true ) { std::cout << errorcode.what(); }                                     REQUIRE(errorcode.failed() == false);

    std::string_view stringSend = "GET /index.html HTTP/1.1\r\nHost: www.example.com\r\nConnection: close\r\n\r\n";
    result_ = AsioSend( socketExample, stringSend );
    if( result_.first == false ) { std::cout << result_.second; }                                               REQUIRE( result_.first == true );

    std::cout << result_.second;
}


Welkin answered 7/9, 2023 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.