C++ Boost/ASIO 服务器与客户端

C++ boost/asio server with client

本文关键字:客户端 服务器 ASIO Boost C++      更新时间:2023-10-16

正在为 p2p 应用程序进行异步网络编程,我遇到了麻烦。我的应用程序必须是服务器和客户端。当服务器收到请求它必须将其广播到其他服务器k。我认为 boost::asio 示例的 HTTP 服务器 3 示例可以很好地工作,其中包含异步客户端的实现(作为一个类(。上面提到的客户端类(来自 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_,"rn",
            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. ClientIO::writeMessage方法向收件人发送错误的信息,因为它
    • 消息分配内存
    • 调用 boost::asio::async_write ,它不发送任何数据,而只将请求放入内部 ASIO 的请求队列,即消息将在某个时间发送。boost::asio::buffer不会复制邮件。它只存储对它的引用。
    • 调用free(message(。即,当排队的写入请求将被执行时,分配给消息的内存可以被覆盖。
  2. ClientIO::handle_after_write 中的内存泄漏。消息已分配但未释放。
  3. ClientIO::readMessageboost::asio::async_read方法不会被strand_.wrap调用包装。

为了避免问题 #1 和 #2 需要使用类似于 ASIO 缓冲区示例的 shared_const_buffer 类的东西。要解决此问题 #3,必须以与boost::asio::async_write调用相同的方式使用 strand_.wrap 调用。