带客户端的C ++ boost / asio服务器

时间:2012-02-15 16:32:56

标签: c++ asynchronous client-server boost-asio

为p2p应用程序进行异步网络编程,我遇到了麻烦。 我的应用程序必须是服务器和客户端。当服务器收到一个 请求它必须将其广播到k其他服务器。我认为boost :: asio示例的HTTP Server 3 Example可以正常工作,其中包含异步客户端(作为类)的实现。 上面提到的客户端类(来自boost :: asio客户端示例)如下:

  ClientIO::ClientIO(boost::asio::io_service& io_service, tcp::resolver::iterator endpoint_iterator)
:   _io_service(io_service),
      strand_(io_service),
      resolver_(io_service),
  socket_(io_service)
  {
  tcp::endpoint endpoint = *endpoint_iterator;
      socket_.async_connect(endpoint,
      boost::bind(&ClientIO::handle_after_connect, this,
      boost::asio::placeholders::error, ++endpoint_iterator));
  }

  void ClientIO::write(G3P mex)
  {
  _io_service.post(boost::bind(&ClientIO::writeMessage, this, mex));
  }

  void ClientIO::writeMessage(G3P mex)
  {
  bool write_in_progress = !messages_queue_.empty();
  messages_queue_.push_back(mex);
  if (!write_in_progress)
  {
    char* message=NULL;
    boost::system::error_code ec;
    if (messages_queue_.front().opcode == DATA)
    {
      message=(char*)malloc((10800)*sizeof(char));
    }
    else
      message=(char*)malloc(1024*sizeof(char));

    boost::asio::streambuf request;
    std::ostream request_stream(&request);
    serializeMessage(message, messages_queue_.front());
    request_stream   << message;
    boost::asio::async_write(socket_, boost::asio::buffer(message, strlen(message)),
    strand_.wrap(
    boost::bind(&ClientIO::handle_after_write, this,
    boost::asio::placeholders::error)));
      free(message);
  }
  }

  void ClientIO::readMessage()
  {
boost::asio::async_read(socket_, data_,
    boost::bind(&ClientIO::handle_after_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
    ));
  }

  void ClientIO::stop()
  {
  socket_.shutdown(tcp::socket::shutdown_both);
  socket_.close();
  }

  void ClientIO::handle_after_connect(const boost::system::error_code& error,
    tcp::resolver::iterator endpoint_iterator)
  {
  if (error)
  {
    if (endpoint_iterator != tcp::resolver::iterator())
    {
      socket_.close();
      tcp::endpoint endpoint = *endpoint_iterator;
      socket_.async_connect(endpoint,
      boost::bind(&ClientIO::handle_after_connect,this,
      boost::asio::placeholders::error, ++endpoint_iterator));
    }
  }
  else
  {
  }
  }

  void ClientIO::handle_after_read(const boost::system::error_code& error, std::size_t bytes_transferred)
  {
  if (bytes_transferred > 0)
  {
    std::istream response_stream(&data_);
    std::string mex="";
    std::getline(response_stream, mex);
    deserializeMessage(&reply_,mex);
    if (reply_.opcode == REPL)
    {
      cout << "ack received" << endl;
    }
  }
  if (error)
  {
    ERROR_MSG(error.message());
  }
  }

  void ClientIO::handle_after_write(const boost::system::error_code& error)
  {
  if (error)
  {
  //            ERROR_MSG("Error in write: " << error.message());
  }
  else
  {
    messages_queue_.pop_front();
    if (!messages_queue_.empty())
    {
      cout << "[w] handle after write" << endl;
      char* message;
      if (messages_queue_.front().opcode == DATA)
      {
    message=(char*)malloc((10800)*sizeof(char));
      }
      else
    message=(char*)malloc(1024*sizeof(char));
      boost::asio::streambuf request;
      std::ostream request_stream(&request);
      serializeMessage(message, messages_queue_.front());
      request_stream << message;
      boost::asio::async_write(socket_, boost::asio::buffer(message, strlen(message)),
      strand_.wrap(
      boost::bind(&ClientIO::handle_after_write, this,
      boost::asio::placeholders::error)));
    }

    boost::asio::async_read_until(socket_, data_,"\r\n",
            strand_.wrap(
            boost::bind(&ClientIO::handle_after_read, this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred)));
  }
  }

  ClientIO::~ClientIO()
  {
  cout << "service stopped" << endl;
  }

}

当服务器收到新请求时,它会启动一个新的DataManagement类表单连接,经过一些计算后,写一个队列 使用上面的类到其他服务器(这里只有一个),每次写入必须对应一个ack

client  --write-> server ---write->\ 
                                    |--server1
                 server <--ACK----</ 

为了实现这一点,我创建了一个io_service实例(io_service_test)作为类变量,在DataManagement构造函数中使用以下代码实例化它:

DataManagement::DataManagement(){
  tcp::resolver resolver(io_service_test);
  tcp::resolver::query query(remotehost, remoteport);
  tcp::resolver::iterator iterator = resolver.resolve(query);
  cluster = new cluster_head::ClusterIO(io_service_test,iterator);
  io_service_test.run_one();
}

然后,在计算之后,发送数据:

 void DataManagement::sendTuple( . . . ){

  . . . 
  io_service_test.reset();
  io_service_test.run();
  for (size_t i=0; i<ready_queue.size() ;i++)
  {
      cluster->write(fragTuple);
  }
}

对应方是以相同方式修改的相同http proxy3示例(没有客户端类)。问题是,有时一切都运行良好,有时它会失败,我得到一个堆栈跟踪,有时它永远不会停止,甚至分段错误。 我认为问题是关闭io_service管理和类方法的生命,但我无法弄清楚。

  • 有什么想法吗?
  • 你有一些适合这种情况的例子,还是一个实现它的虚拟类?

1 个答案:

答案 0 :(得分:1)

简要回顾一下代码,我看到了以下问题。

  1. ClientIO::writeMessage方法向收件人发送错误信息,因为它
    • 消息分配内存
    • 调用boost::asio::async_write,它不发送任何数据,只是将请求发送到内部ASIO的请求队列,即消息将在某个时间发送。 boost::asio::buffer不会复制消息。它只存储对它的引用。
    • 致电free(message)。即,当执行排队的写请求时,可以覆盖分配给消息的内存。
  2. ClientIO::handle_after_write中的内存泄漏。 消息已分配但未释放。
  3. boost::asio::async_read的{​​{1}}方法未被ClientIO::readMessage电话打包。
  4. 为了避免问题,需要使用#1和#2来使用类似ASIO缓冲区示例的strand_.wrap类。 要解决问题,必须按照与shared_const_buffer来电相同的方式使用strand_.wrap电话。