boost::asio async_read_some 工作一次然后停止工作,为什么? (使用 shared_ptr)

boost::asio async_read_some works once then stops working, why? (uses shared_ptr)

我一直在尝试从http://think-async.com/asio/boost_asio_1_3_0/doc/html/boost_asio/example/http/server/connection.cpp开始,并根据我的需要进行修改。我想一个接一个地接受多个客户端,并最终通过 TCP 读取数据。如果我 运行 它然后在另一个 shell 使用

我的服务器工作正常
nc localhost 3731

然后按 Control-C。第一个客户端已正确断开连接。然后我再次输入相同的命令。麻烦来了下一个客户;它接受并打印出阅读的开始,但是当我在第二个客户端上点击 control-C 时,它似乎没有收到断开连接消息。我不知道为什么。 boost::asio async_read_some 有什么帮助吗?

这是显示第一个客户端正常工作然后后续客户端未正确断开连接的输出调试消息:

./a.out                                                          
Made connection       at address 80x2240370
Added connection to ConnectionManager at 80x2240370
  Starting read at address 80x2240370
Made connection       at address 80x22449f0
Did read of 0 with error code asio.misc:2
Accepting bytes.
Removing connection to ConnectionManager at 80x2240370
Destroying connection at address 80x2240370
Added connection to ConnectionManager at 80x22449f0
  Starting read at address 80x22449f0
Made connection       at address 80x2248af0

这里是为简洁起见而编辑的完整源代码,但仍然显示问题:

// compile with: g++ asiohelp.cpp -lboost_system -lpthread  -std=c++11

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <set>
#include <vector>
#include <string>

#define CONTROL_PORT "3731"
#define CONTROL_BIND_ADDRESS "127.0.0.1"

namespace hsd {
namespace net {

class HSDConnectionManager;

class HSDConnection: public boost::enable_shared_from_this<HSDConnection> {
  boost::asio::ip::tcp::socket socket_;
  boost::array<char, 16384> readBuffer_;
  HSDConnectionManager& connectionManager_;
public:
  explicit HSDConnection(boost::asio::io_service& io_service,
      HSDConnectionManager& manager);
  ~HSDConnection(void);

  boost::asio::ip::tcp::socket& socket(void);

  /// Start the first asynchronous operation for the connection.
  void startRead(void);
  void handleRead(const boost::system::error_code& e,
      std::size_t bytesTransferred);
  void stop(void);
private:
  void continueRead(void);

};

typedef boost::shared_ptr<HSDConnection> ConnectionPtr;
}
}


namespace hsd {
namespace net {

class HSDConnectionManager: private boost::noncopyable {
public:
  void start(ConnectionPtr c);

  void stop(ConnectionPtr c);

  void stop_all();

private:
  std::set<ConnectionPtr> connections_;
};

}
}

namespace hsd {
namespace net {

class ControlServer {
public:
  // Construct the server to listen on the specified TCP address and port
  explicit ControlServer(boost::asio::io_service& io_service_,
      const std::string& address, const std::string& port);
  virtual ~ControlServer(void);
  // Run the server's io_service loop.
  void run();

  // Stop the server.
  void stop();

  // Handle packet
  virtual void packetReceived(std::string result);

private:
  /// Handle completion of an asynchronous accept operation.
  void handleAccept(const boost::system::error_code& e);

  /// Handle a request to stop the server.
  void handleStop();

  /// The io_service used to perform asynchronous operations.
  boost::asio::io_service io_service_;

  /// Acceptor used to listen for incoming connections.
  boost::asio::ip::tcp::acceptor acceptor_;

  /// The connection manager which owns all live connections.
  HSDConnectionManager connectionManager_;

  /// The next connection to be accepted.
  ConnectionPtr nextConnection_;

};

}

}


using namespace std;
using namespace boost::system::errc;

namespace hsd {
namespace net {

ControlServer::ControlServer(boost::asio::io_service& io_service_,
    const std::string& address, const std::string& port) :
    io_service_(), acceptor_(io_service_), connectionManager_(), nextConnection_() {
  nextConnection_ = ConnectionPtr(
      new HSDConnection(io_service_, connectionManager_));
  // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
  boost::asio::ip::tcp::resolver resolver(io_service_);
  boost::asio::ip::tcp::resolver::query query(address, port);
  boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
  acceptor_.open(endpoint.protocol());
  acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
  acceptor_.bind(endpoint);
  acceptor_.listen();

  acceptor_.async_accept(nextConnection_->socket(),
      boost::bind(&ControlServer::handleAccept, this,
          boost::asio::placeholders::error));

}

ControlServer::~ControlServer(void) {
  cout << "Destroying ControlServer\n";
}

void ControlServer::handleAccept(const boost::system::error_code& e) {
  if (e == success) {
    connectionManager_.start(nextConnection_);
    nextConnection_.reset(
        new HSDConnection(io_service_, connectionManager_));
    //nextConnection_ = ConnectionPtr(new HSDConnection(io_service_,
    //             connectionManager_, *hsPacketReaderListener_s));
    acceptor_.async_accept(nextConnection_->socket(),
        boost::bind(&ControlServer::handleAccept, this,
            boost::asio::placeholders::error));
  }
}

void ControlServer::packetReceived(std::string result) {
  cout << "Got packet: " << result << "\n";

}

}
}


using namespace std;

namespace hsd {
namespace net {
boost::asio::ip::tcp::socket& HSDConnection::socket(void) {
  return socket_;
}

HSDConnection::HSDConnection(boost::asio::io_service& io_service,
    HSDConnectionManager& manager) :
    socket_(io_service), connectionManager_(manager), readBuffer_()

{
  cout << "Made connection       at address " << ios::hex << this << "\n";
}

HSDConnection::~HSDConnection(void) {
  cout << "Destroying connection at address " << ios::hex << this << "\n";
}

void HSDConnection::handleRead(const boost::system::error_code& e,
    std::size_t bytesTransferred) {
  cout << "Did read of " << bytesTransferred << " with error code " << e
      << "\n";
  std::string byteString(readBuffer_.data(), bytesTransferred);
  vector<string> result;
  cout << "Accepting bytes.\n";
  //hsPacketCore_.acceptBytes(byteString, result);
  for (string &packet : result) {
    //listener_->packetReceived(packet);
  }
  if (e == boost::system::errc::success
      || e == boost::asio::error::operation_aborted) {
    continueRead();
  } else if (bytesTransferred == 0) {
    connectionManager_.stop(shared_from_this());
  }
}

void HSDConnection::continueRead(void) {
  cout << "Continuing read at address " << ios::hex << this << "\n";
  socket_.async_read_some(boost::asio::buffer(readBuffer_),
      boost::bind(&HSDConnection::handleRead, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
}

void HSDConnection::startRead(void) {
  cout << "  Starting read at address " << ios::hex << this << "\n";

  socket_.async_read_some(boost::asio::buffer(readBuffer_),
      boost::bind(&HSDConnection::handleRead, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
}

void HSDConnection::stop(void) {
  socket_.close();
}
}
}

namespace hsd {
namespace net {

void HSDConnectionManager::start(ConnectionPtr c) {
  connections_.insert(c);
  std::cout << "Added connection to ConnectionManager at " << std::ios::hex << c
      << "\n";
  c->startRead();
}

void HSDConnectionManager::stop(ConnectionPtr c) {
  std::cout << "Removing connection to ConnectionManager at " << std::ios::hex
      << c << "\n";
  connections_.erase(c);
  c->stop();
}
}
}


using boost::asio::ip::tcp;
using namespace std;
using namespace hsd::net;

int main()
{

  try
  {
    // We need to create a server object to accept incoming client connections.
    boost::asio::io_service io_service;

    // The io_service object provides I/O services, such as sockets,
    // that the server object will use.
    ControlServer server(io_service, CONTROL_BIND_ADDRESS, CONTROL_PORT);

    // Run the io_service object to perform asynchronous operations.
    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

您有两个 io_service 实例。行为上的差异是因为您在从两个不同的 io_service 实例初始化的套接字上调用 async_accept 。混淆来自您将参数 io_service_ 命名为成员变量的方式。我设法通过将成员变量设为引用来修复您的代码。


    /// The io_service used to perform asynchronous operations.
    boost::asio::io_service& io_service_;

然后在构造函数的形参中消除变量的歧义,并用这个参数初始化成员变量。


    ControlServer::ControlServer(boost::asio::io_service& io_service, 
        const std::string& address, const std::string& port) :
        io_service_(io_service), acceptor_(io_service),
        connectionManager_(), nextConnection_() {

另一种选择是公开 io_service 成员变量,然后 运行 即 io_service.


    // The io_service object provides I/O services, such as sockets,
    // that the server object will use.
    ControlServer server(CONTROL_BIND_ADDRESS, CONTROL_PORT);
    
    // Run the io_service object to perform asynchronous operations.
    server.io_service().run();

我希望这是有道理的,我上周才第一次使用 boost::asio,写了一篇类似的文章。