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

时间:2015-07-12 05:19:45

标签: c++ boost boost-asio

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

,我的服务器工作正常
nc localhost 3731
然后点击控制-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;
}

1 个答案:

答案 0 :(得分:2)

您有两个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,写了类似的东西。

https://dabblingseriously.wordpress.com/2015/07/06/a-minimal-http-web-server-using-boostasio/