Search code examples
c++boostboost-asio

boost::async_write causes error An existing connection was forcibly closed by the remote host


I want to send a struct from client to server using boost::asio. I followed boost tutorial link https://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/examples.html#boost_asio.examples.serialization. I slighty modified the code in server.cpp and client.cpp. With the new code, after a connection is established, client.cpp writes the struct stock to server and reads stock information at server side. (in the tutorial version, after a connection established, the server writes stock struct to client and client reads them. This version works for me.)

My problem is that after a connection is established, the async_write in client.cpp causes error

Error in write: An existing connection was forcibly closed by the remote host

and the async_read in server.cpp causes error

Error in read:The network connection was aborted by the local system.

As suggested by some forum answers, I changed this pointers in function handlers of async_write and async_read to shared_from_this. Still the problem exists.

I am not able to identify whether the client or the server side is causing problem. Please help.

server.cpp

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
#include "connection.h" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "stock.h"

namespace s11n_example
{
    /// Serves stock quote information to any client that connects to it.
    class server : public boost::enable_shared_from_this<server>
    {
        private:
            /// The acceptor object used to accept incoming socket connections.
            boost::asio::ip::tcp::acceptor acceptor_;
            /// The data to be sent to each client.
            std::vector<stock> stocks_;

        public:
            /// Constructor opens the acceptor and starts waiting for the first incoming
            /// connection.
            server(boost::asio::io_service& io_service, unsigned short port):
            acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
            {
                // Start an accept operation for a new connection.
                connection_ptr new_conn(new connection(acceptor_.get_io_service()));
                acceptor_.async_accept(new_conn->socket(),
                    boost::bind(&server::handle_accept, this,boost::asio::placeholders::error, new_conn));
            }

            /// Handle completion of a accept operation.
            void handle_accept(const boost::system::error_code& e, connection_ptr conn)
            {
                if (!e)
                {
                    std::cout <<  "Received a connection" <<std::endl;
                    conn->async_read(stocks_,
                         boost::bind(&server::handle_read, shared_from_this(),boost::asio::placeholders::error));
                }
                // Start an accept operation for a new connection.
                connection_ptr new_conn(new connection(acceptor_.get_io_service()));
                acceptor_.async_accept(new_conn->socket(),
                    boost::bind(&server::handle_accept, this,boost::asio::placeholders::error, new_conn));
            }
            /// Handle completion of a read operation.
            void handle_read(const boost::system::error_code& e)
            {
                if (!e)
                {
                    // Print out the data that was received.
                    for (std::size_t i = 0; i < stocks_.size(); ++i)
                    {
                        std::cout << "Stock number " << i << "\n";
                        std::cout << "  code: " << stocks_[i].code << "\n";
                        std::cout << "  name: " << stocks_[i].name << "\n";
                        std::cout << "  open_price: " << stocks_[i].open_price << "\n";
                        std::cout << "  high_price: " << stocks_[i].high_price << "\n";
                        std::cout << "  low_price: " << stocks_[i].low_price << "\n";
                        std::cout << "  last_price: " << stocks_[i].last_price << "\n";
                        std::cout << "  buy_price: " << stocks_[i].buy_price << "\n";
                        std::cout << "  buy_quantity: " << stocks_[i].buy_quantity << "\n";
                        std::cout << "  sell_price: " << stocks_[i].sell_price << "\n";
                        std::cout << "  sell_quantity: " << stocks_[i].sell_quantity << "\n";
                    }
                }
                else
                {
                    // An error occurred.
                    std::cerr << "Error in read:" << e.message() << std::endl;
                }
            }
    };

} // namespace s11n_example
int main(int argc, char* argv[])
{
    try
    {
        // Check command line arguments.
        if (argc != 2)
        {
            std::cerr << "Usage: server <port>" << std::endl;
            return 1;
        }
        unsigned short port = boost::lexical_cast<unsigned short>(argv[1]);

        boost::asio::io_service io_service;
        boost::shared_ptr<s11n_example::server> server(new s11n_example::server(io_service, port));
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    return 0;
}

client.cpp

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
#include "connection.h" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include  <boost/enable_shared_from_this.hpp>
#include "stock.h"

namespace s11n_example {

    /// Downloads stock quote information from a server.
    class client : public boost::enable_shared_from_this<client>
    {
        private:
            /// The connection to the server.
            connection connection_;
            /// The data received from the server.
            std::vector<stock> stocks_;

        public:
            /// Constructor starts the asynchronous connect operation.
            client(boost::asio::io_service& io_service, const std::string& host, const std::string& service)
            : connection_(io_service)
            {
                // Resolve the host name into an IP address.
                boost::asio::ip::tcp::resolver resolver(io_service);
                boost::asio::ip::tcp::resolver::query query(host, service);
                boost::asio::ip::tcp::resolver::iterator endpoint_iterator =
                resolver.resolve(query);
                // Start an asynchronous connect operation.
                boost::asio::async_connect(connection_.socket(), endpoint_iterator,
                    boost::bind(&client::handle_connect, this,boost::asio::placeholders::error));
            }
            /// Handle completion of a connect operation.
            void handle_connect(const boost::system::error_code& e) //, connection_ptr conn
            {
                if (!e)
                {
                    std::cout << "Connected to server!" << std::endl;
                    // Create the data to be sent to each client.
                    stock s;
                    s.code = "ABC";
                    s.name = "A Big Company";
                    s.open_price = 4.56;
                    s.high_price = 5.12;
                    s.low_price = 4.33;
                    s.last_price = 4.98;
                    s.buy_price = 4.96;
                    s.buy_quantity = 1000;
                    s.sell_price = 4.99;
                    s.sell_quantity = 2000;
                    stocks_.push_back(s);

                    s.code = "DEF";
                    s.name = "Developer Entertainment Firm";
                    s.open_price = 20.24;
                    s.high_price = 22.88;
                    s.low_price = 19.50;
                    s.last_price = 19.76;
                    s.buy_price = 19.72;
                    s.buy_quantity = 34000;
                    s.sell_price = 19.85;
                    s.sell_quantity = 45000;
                    stocks_.push_back(s);

                    // Successfully established connection. Start operation to write the list
                    // of stocks.
                    connection_.async_write(stocks_,
                    boost::bind(&client::handle_write, shared_from_this(),boost::asio::placeholders::error)); //,&conn )
                }
                else
                {
                      // An error occurred. Log it and return.
                      std::cerr << "Error in connecting to server" << e.message() << std::endl;
                }
            }

            /// Handle completion of a write operation.
            void handle_write(const boost::system::error_code& e)//, connection* conn
            {
                if (!e)
                {
                    std::cout << "Finished writing to server" << std::endl;
                }
                else
                {
                  // An error occurred. Log it and return. Since we are not starting a new
                  // operation the io_service will run out of work to do and the client will
                  // exit.
                  std::cerr << "Error in write: " << e.message() << std::endl;
                }

                // Nothing to do. The socket will be closed automatically when the last
                // reference to the connection object goes away.
            }
    };

} // namespace s11n_example

int main(int argc, char* argv[])
{
    try
    {
        // Check command line arguments.
        if (argc != 3)
        {
            std::cerr << "Usage: client <host> <port>" << std::endl;
            return 1;
        }

        boost::asio::io_service io_service;
        //s11n_example::client client(io_service, argv[1], argv[2]);
        boost::shared_ptr<s11n_example::client> client(new s11n_example::client(io_service, argv[1], argv[2]));
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

Thanks.


Solution

  • You need to pass conn to handle read otherwise it will be destructed at the end of the handle_accept method. When it's destructed the socket it contains will also be destructed and the connection will close.

    conn->async_read(stocks_,
         boost::bind(&server::handle_read, shared_from_this(), conn, boost::asio::placeholders::error));
    

    Lambdas make this easier to read than using bind:

    auto self = shared_from_this();
    conn->async_read(stocks_,
    [self, this, conn] (boost::system::error_code ec) { handle_read(ec); });
    

    The variables listed in the capture list will be copied so the shared pointers will be kept alive.