0

I am following the example from http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/serialization/connection.hpp. I have modified the connection.hpp header to insert to more operations called aynchronous_read and asynchronous_write.The code is attached here...

    //
// connection.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef SERIALIZATION_CONNECTION_HPP
#define SERIALIZATION_CONNECTION_HPP

#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <iomanip>
#include <string>
#include <sstream>
#include <vector>

namespace s11n_example {

/// The connection class provides serialization primitives on top of a socket.
/**
 * Each message sent using this class consists of:
 * @li An 8-byte header containing the length of the serialized data in
 * hexadecimal.
 * @li The serialized data.
 */
class connection
{
public:
  /// Constructor.
  connection(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  /// Get the underlying socket. Used for making a connection or for accepting
  /// an incoming connection.
  boost::asio::ip::tcp::socket& socket()
  {
    return socket_;
  }

  /// Asynchronously write a data structure to the socket.
  template <typename T, typename Handler>
  void async_write(const T& t, Handler handler)
  {
    // Serialize the data first so we know how large it is.
    std::ostringstream archive_stream;
    boost::archive::text_oarchive archive(archive_stream);
    archive << t;
    outbound_data_ = archive_stream.str();

    // Format the header.
    std::ostringstream header_stream;
    header_stream << std::setw(header_length)
      << std::hex << outbound_data_.size();
    if (!header_stream || header_stream.str().size() != header_length)
    {
      // Something went wrong, inform the caller.
      boost::system::error_code error(boost::asio::error::invalid_argument);
      socket_.get_io_service().post(boost::bind(handler, error));
      return;
    }
    outbound_header_ = header_stream.str();

    // Write the serialized data to the socket. We use "gather-write" to send
    // both the header and the data in a single write operation.
    std::vector<boost::asio::const_buffer> buffers;
    buffers.push_back(boost::asio::buffer(outbound_header_));
    buffers.push_back(boost::asio::buffer(outbound_data_));
    boost::asio::async_write(socket_, buffers, handler);
  }

  ///Asynchronously write a string to the socket.
  template <typename T, typename Handler>
  void asyncronous_write(const T& t, Handler handler)
  {
    std::ostringstream archive_stream;
    boost::archive::text_oarchive archive(archive_stream);
    archive << t;
    outbound_str_ = archive_stream.str();
    //boost::asio::buffer buffer1(outbound_str_);
    boost::asio::async_write(socket_,boost::asio::buffer(outbound_str_), handler);
  }
  /// Asynchronously read a string from the socket.
  template <typename T, typename Handler>
  void asyncronous_read(T& t, Handler handler)
  {
    // Issue a read operation to read exactly the number of bytes in a header.
    void (connection::*f)(
        const boost::system::error_code&,
        T&, boost::tuple<Handler>)
      = &connection::handle_read_str<T, Handler>;
    boost::asio::async_read(socket_, boost::asio::buffer(inbound_str_),
        boost::bind(f,
          this, boost::asio::placeholders::error, boost::ref(t),
          boost::make_tuple(handler)));
  }

  /// Handle a completed read of message data.
  template <typename T, typename Handler>
  void handle_read_str(const boost::system::error_code& e,
      T& t, boost::tuple<Handler> handler)
  {
    if (e)
    {
      boost::get<0>(handler)(e);
    }
    else
    {
      // Extract the data structure from the data just received.
      try
      {
        std::string archive_data(&inbound_str_[0], inbound_str_.size());
        std::istringstream archive_stream(archive_data);
        boost::archive::text_iarchive archive(archive_stream);
        archive >> t;
      }
      catch (std::exception& e)
      {
        // Unable to decode data.
        boost::system::error_code error(boost::asio::error::invalid_argument);
        boost::get<0>(handler)(error);
        return;
      }

      // Inform caller that data has been received ok.
      boost::get<0>(handler)(e);
    }
  }



  /// Asynchronously read a data structure from the socket.
  template <typename T, typename Handler>
  void async_read(T& t, Handler handler)
  {
    // Issue a read operation to read exactly the number of bytes in a header.
    void (connection::*f)(
        const boost::system::error_code&,
        T&, boost::tuple<Handler>)
      = &connection::handle_read_header<T, Handler>;
    boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
        boost::bind(f,
          this, boost::asio::placeholders::error, boost::ref(t),
          boost::make_tuple(handler)));
  }

  /// Handle a completed read of a message header. The handler is passed using
  /// a tuple since boost::bind seems to have trouble binding a function object
  /// created using boost::bind as a parameter.
  template <typename T, typename Handler>
  void handle_read_header(const boost::system::error_code& e,
      T& t, boost::tuple<Handler> handler)
  {
    if (e)
    {
      boost::get<0>(handler)(e);
    }
    else
    {
      // Determine the length of the serialized data.
      std::istringstream is(std::string(inbound_header_, header_length));
      std::size_t inbound_data_size = 0;
      if (!(is >> std::hex >> inbound_data_size))
      {
        // Header doesn't seem to be valid. Inform the caller.
        boost::system::error_code error(boost::asio::error::invalid_argument);
        boost::get<0>(handler)(error);
        return;
      }

      // Start an asynchronous call to receive the data.
      inbound_data_.resize(inbound_data_size);
      void (connection::*f)(
          const boost::system::error_code&,
          T&, boost::tuple<Handler>)
        = &connection::handle_read_data<T, Handler>;
      boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_),
        boost::bind(f, this,
          boost::asio::placeholders::error, boost::ref(t), handler));
    }
  }

  /// Handle a completed read of message data.
  template <typename T, typename Handler>
  void handle_read_data(const boost::system::error_code& e,
      T& t, boost::tuple<Handler> handler)
  {
    if (e)
    {
      boost::get<0>(handler)(e);
    }
    else
    {
      // Extract the data structure from the data just received.
      try
      {
        std::string archive_data(&inbound_data_[0], inbound_data_.size());
        std::istringstream archive_stream(archive_data);
        boost::archive::text_iarchive archive(archive_stream);
        archive >> t;
      }
      catch (std::exception& e)
      {
        // Unable to decode data.
        boost::system::error_code error(boost::asio::error::invalid_argument);
        boost::get<0>(handler)(error);
        return;
      }

      // Inform caller that data has been received ok.
      boost::get<0>(handler)(e);
    }
  }

private:
  /// The underlying socket.
  boost::asio::ip::tcp::socket socket_;

  /// The size of a fixed length header.
  enum { header_length = 8 };

  /// Holds an outbound header.
  std::string outbound_header_;

  /// Holds the outbound data.
  std::string outbound_data_;
  std::string outbound_str_;

  /// Holds an inbound header.
  char inbound_header_[header_length];

  /// Holds the inbound data.
  std::vector<char> inbound_data_;
  std::vector<char>inbound_str_;
  //char* inbound_str_=new char[1024];
};

typedef boost::shared_ptr<connection> connection_ptr;

} // namespace s11n_example

#endif // SERIALIZATION_CONNECTION_HPP

The server and client codes are ---

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


namespace s11n_example {

/// Serves stock quote information to any client that connects to it.
class server
{
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))
  {
    // Create the data to be sent to each client.
     std::string str;
     std::ifstream in;
     stock s;
     in.open("aces.clf");
     if(!in)
     {
     std::cout<<"Error in Opening a file"<<std::endl;
     exit(1);
     }
     for(int i=0;i<13;i++)
     {
      getline(in,str);
     }
     while(!in.eof())
     {
        getline(in,str);
        s.data.append(str);
        getline(in,str);
        s.data.append(str);
        s.d_size=s.data.size();
        stocks_.push_back(s);
        s.data.clear();
     }

    // 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)
  {
   std::string buf1;
    if (!e)
    {

      // Successfully accepted a new connection. Send the list of stocks to the
      // client. The connection::async_write() function will automatically
      // serialize the data structure for us.
    for(int i=0;i<5;i++)
     {

      conn->async_write(stocks_[i],
          boost::bind(&server::handle_write, this,
            boost::asio::placeholders::error, conn));

      usleep(500000);
      conn->async_read(buf1,
          boost::bind(&server::handle_read, this,
            boost::asio::placeholders::error, conn));
       std::cout<<buf1<<"\n"<<std::endl;
    }

      // 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));
    }
    else
    {
      // An error occurred. Log it and return. Since we are not starting a new
      // accept operation the io_service will run out of work to do and the
      // server will exit.
      std::cerr << e.message() << std::endl;
    }
  }

  /// Handle completion of a write operation.
  void handle_write(const boost::system::error_code& e, connection_ptr conn)
  {

      //conn->async_read(buf1,
          //boost::bind(&server::handle_read, this,
           // boost::asio::placeholders::error, conn));
     // std::cout<<buf1<<"\n"<<std::endl;

    }

///handle completion of read operation.
void handle_read(const boost::system::error_code& e,connection_ptr conn)
{
 //if(!e)
    //{
          //std::cout<<"\n"<<stocks_1.data<<std::endl;
          //stocks_1.data.clear();
    //}
         //{
           //if (buf1.compare("data received and processed")==0)
            //{
                          // buf1.clear();
                            //std::cout<<buf1<<"\n"<<std::endl;

            //}
           //else
            //{
                //std::cout<<"Error occurred"<<std::endl;
                //exit(1);
           // }
    //}
}

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_;
};

} // 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;
    s11n_example::server server(io_service, port);
    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}






#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <fstream>
#include <vector>
#include "connection.hpp" // Must come before boost/serialization headers.
#include <boost/serialization/vector.hpp>
#include "structsample.hpp"

struct stock1
  {
    std::string data;
    int d_size;
   template <typename Archive>
  void serialize(Archive& ar, const unsigned int version)
  {
    ar & data;
    ar & d_size;

  }
} stocks_1;
namespace s11n_example {

/// Downloads stock quote information from a server.
class client
{
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);
    boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;

// Start an asynchronous connect operation.
    connection_.socket().async_connect(endpoint,
        boost::bind(&client::handle_connect, this,
          boost::asio::placeholders::error, ++endpoint_iterator));
  }

  /// Handle completion of a connect operation.
  void handle_connect(const boost::system::error_code& e,
      boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
  {
    if (!e)
    {
      // Successfully established connection. Start operation to read the list
      // of stocks. The connection::async_read() function will automatically
      // decode the data that is read from the underlying socket.
      connection_.async_read(stocks_1,
          boost::bind(&client::handle_read, this,
            boost::asio::placeholders::error));

    }
    else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
    {
      // Try the next endpoint.
      connection_.socket().close();
      boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
      connection_.socket().async_connect(endpoint,
          boost::bind(&client::handle_connect, this,
            boost::asio::placeholders::error, ++endpoint_iterator));
    }

 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 << e.message() << std::endl;
    }
  }

  /// Handle completion of a read operation.
  void handle_read(const boost::system::error_code& e)
  {
   std::string buf2;
    if (!e)
    {

        std::cout << "  data: " << stocks_1.data << "\n";
        std::cout << "  size: " << stocks_1.d_size << "\n";

        usleep(500000);
      if(!e)
      {
       //stocks_1.data="data received and processed";
       //tocks_1.d_size=stocks_1.data.size();
       connection_.async_write(buf2,
          boost::bind(&client::handle_write, this,
            boost::asio::placeholders::error));
       std::cout<<buf2<<std::endl;
        //buf2.clear();
       // connection_.async_write(stocks_1,
          //boost::bind(&client::handle_write, this,
            //boost::asio::placeholders::error));
       //stocks_1.data.clear();
      }
    }
      else
    {
      // An error occurred.
      std::cerr << e.message() << std::endl;
    }


// Since we are not starting a new operation the io_service will run out of
    // work to do and the client will exit.

  }

void handle_write(const boost::system::error_code& e)
{
  if(!e)
{
          connection_.async_read(stocks_1,
          boost::bind(&client::handle_read, this,
            boost::asio::placeholders::error));
}
else
    //std::cout<<"Error is there"<<std::endl;
        exit(1);
}
private:
  /// The connection to the server.
  connection connection_;

  /// The data received from the server.
  std::vector<stock> stocks_;


};

} // 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]);
    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

structsample.hpp----

#ifndef SERIALIZATION_STOCK_HPP
#define SERIALIZATION_STOCK_HPP

#include <string>

namespace s11n_example {

/// Structure to hold information about a single stock.
struct stock
{
 public:

  std::string data;
  int d_size;

  template <typename Archive>
  void serialize(Archive& ar, const unsigned int version)
  {
    ar & data;
    ar & d_size;

  }
};

} // namespace s11n_example

#endif // SERIALIZATION_STOCK_HPP

Here the data is reaching from server to client but the response from client is not sent back.

  • std::string is not directly supported as an asio buffer, read this: http://stackoverflow.com/questions/4068249/how-to-use-stdstring-with-asiobuffer – Sven Nilsson Jan 24 '17 at 10:24
  • Can you please tell me, here where does the problem lie in...handle_read_str or asynchonous read.......and what to change...should I conver to char *???? –  Jan 24 '17 at 10:35
  • @SvenNilsson std::string is supported by asio::buffer, but only for immutable buffers (i.e. for writes). – Richard Hodges Jan 24 '17 at 10:41
  • yeah with a fixed size string, by taking the address of the first char. I would still recommend to use std::vector, std::array etc – Sven Nilsson Jan 24 '17 at 10:44
  • @SvenNilsson no, asio::buffer(some_string) will return an asio::const_buffers_1 which represents all characters in the string. calling this on a temporary is of course an error. – Richard Hodges Jan 24 '17 at 12:21

1 Answers1

2

What ASIO really transfers is bytes of data. A string is not a suitable container for storing arbitrary data (which may contain null characters and will cause confusing results if contained in the string).

To transfer a string using boost::asio, it is better to use a buffer based on std::vector or std::array, and then analyze the data in this buffer to obtain your string.

Sven Nilsson
  • 1,861
  • 10
  • 11
  • Instead of using the std::string I have used char * and it solved the problem. Thank you, but now after calling these functions from client and server, the server is not bring able to asynchronously read the string sent from the client side. –  Jan 24 '17 at 11:40
  • is the server receiving any data in the buffer? – Sven Nilsson Jan 24 '17 at 11:42
  • yess..it is..Should I edit the question by attaching the server and client's code???? –  Jan 24 '17 at 11:50
  • so what is in the buffer? wrong data? try print the characters received, or use a debugger to look at it. – Sven Nilsson Jan 24 '17 at 13:03
  • I have tried to debug it, but probably I am not getting where the exact fault lies. I think in buffer it is not reading anything, hence while printing it is simply printing blank line. If possible can u please debug and edit it as required. Thank you. –  Jan 25 '17 at 05:59
  • I cannot debug, your code is incomplete. However, I see some problems: the buffer used with async_write/async_read must NOT be a temporary variable. It must be member of class or global variable, so that it remain valid until handle_write/handle_read have been called. – Sven Nilsson Jan 25 '17 at 08:34
  • I have attached all the four components of the code for your convenience...For async_read and async write the output is coming actually...in case of asyncronous_read and asynchronous_write it is not working. In these cases, the buffer is used in the mentioned scope only.@Sven –  Jan 25 '17 at 08:56
  • You should not use asyncronous_read and asynchronous_write, because they use strings, which is bad idea. Copy your string into a vector, and use async_write. Then read your string from the vector in async_read. Do you need help on how to copy string into vector? – Sven Nilsson Jan 25 '17 at 10:33
  • No I have modified the code using vector and async_read and async_write also, but still the message is not getting printed in server side. –  Jan 25 '17 at 12:00