2015-07-12 108 views
1

我一直试图从http://think-async.com/asio/boost_asio_1_3_0/doc/html/boost_asio/example/http/server/connection.cpp开始,并根据我的需要进行修改。我想一个接一个地接受多个客户端,并最终通过TCP读取数据。我的服务器工作正常,如果我运行它,然后在另一个外壳使用boost :: asio async_read_some有效,然后停止工作,为什么? (使用shared_ptr)

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

回答

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/

+0

太棒了!伟大的调试,谢谢你我的朋友! –

+0

我喜欢读博客。关于博客条目,Asio的一个常见习惯是使用'enable_shared_from_this'来允许对象在异步调用链中延长自己的生命。有关示例,请参阅官方教程的[异步TCP日间服务器](http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html)。 –

+0

谢谢!几天前我发现了shared_from_this(),并且也在尝试使用它,这是一个非常好的功能,我打算将它包含在我的下一篇文章中。在过去,我避免使用各种设计,因为我无法从类范围内获得shared_ptr,现在可以使用它们。我也开始使用boost :: bind而不是原始lambda表达式,并且最终得到更干净的代码,并且少了一点这样:-) –

相关问题