我正在使用boost.asio,并且我有一个简单的服务器,需要处理多个连接。在开始“已连接”状态之前,我需要在有限的时间内与客户端进行握手。我在握手的每个步骤中都使用了链式异步操作,因为我需要使用一个计时器,并且(据我所知,同步读取和写入无法做到这一点)。我需要一种方法来阻塞每个连接,直到截止期限计时器结束或握手成功为止,而不会阻塞服务器。
有什么办法可以做到这一点?
UPDATE 一些代码可以澄清事情
一个简单的服务器
typedef boost::asio::ip::tcp::socket sock;
class server
{
public:
server() : _ios(), _acceptor(_ios)
{
boost::asio::ip::tcp::resolver resolver(_ios);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), PORT);
_acceptor.open(endpoint.protocol());
_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
_acceptor.bind(endpoint);
_acceptor.listen();
do_accept();
}
void run()
{
_ios.run();
}
private:
void do_accept()
{
auto socket = std::make_shared<TSock>(_ios);
_acceptor.async_accept(*socket, std::bind(&server::handle_accept,
this, socket, std::placeholders::_1));
}
void handle_accept(std::shared_ptr<sock> socket, const boost::system::error_code& ec)
{
auto connection = std::make_shared<CCon>();
if (connection->Accept(socket))
{
std::cout << "Connection accepted" << std::endl;
}
else
{
std::cout << "Connection not accepted" << std::endl;
}
do_accept();
}
boost::asio::io_service _ios;
boost::asio::ip::tcp::acceptor _acceptor;
std::set<std::shared_ptr<CCon>> _connections;
};
int32_t main(int32_t argc, char *argv[])
{
server s;
s.run();
}
连接->接受(套接字)解释
bool CCon::Accept(<std::shared_ptr<sock>> tcpSocket)
{
// set handshake sequence
SendGreeting();
// I NEED TO WAIT HERE UNTIL connectionAccepted gets a value
if (connectionAccepted)
{
// Connection Accepted
return(true)
}
else
{
//Connection Rejected
return(false)
}
}
SendGreeting()包含
boost::asio::async_write(*tcpSocket,
boost::asio::buffer(oBuff,bytesBuffered),
std::bind(&CCon::WaitForResp,
this, std::placeholders::_1));
问题在于,永远不会调用WaitForResp。只有在设置新处理程序后重新启动
io_service
(stop()
-> reset()
-> run()
)时,它才会被调用,这根本不是解决方案。我想我缺少有关Asio的东西,如果有人可以帮助我,我会感到很高兴。
最佳答案
您的示例代码不完整,所以我不能保证我的答案是解决方案,但是我会指出一些我认为是问题的东西。
第一点-io_service
不仅保持运行。如果它认为没有更多的工作要做(提示中什么也没有),那么::run()
将退出,并且由于您不希望这样做,所以您将得到意外的行为。您需要通过创建并保持io_service::work
对象处于 Activity 状态来防止它耗尽工作。从文档:
boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);
...
有关here的更多信息。
其次,我担心
CCon
对象的生存期如何保持。谁负责保持它的生命?我问是因为您要绑定(bind)到其中的this
,所以您在内部产生了异步回调,但显然依赖于外部对象来确保this
在回调返回时仍处于 Activity 状态。如果您未能正确执行此操作,那么您将获得未定义的行为,这不一定意味着您将获得大量的崩溃和错误代码,但是也许处理程序会永久完成或挂起,或者由于状态未定义,其他一些奇怪的不确定性行为。根据此处不完整的代码,似乎没有什么可以使
CCon
在外部保持 Activity 状态,这意味着事情可能超出范围并被破坏了。我要猜测的是,您的服务器始终会出现“不接受连接”的情况,如果有的话。如果您分解了此处的代码,则会生成包装在CCon
中的shared_ptr
,然后调用::Accept()
,这又会调用非阻塞异步方法,这些方法可能立即完成,或者从现在起100年后完成。由于它们是非阻塞,因此代码将立即进入if(connectionAccepted)
语句,并因此立即立即返回另一种方式,这时您的shared_ptr
的CCon
超出了服务器handle_accept
的范围,因此ref计数递减为0并调用析构函数。您应该做的是使
CCon
从std::enable_shared_from_this
或boost::enable_shared_from_this
继承,并且在CCon
内部绑定(bind)的所有异步回调中,您应该绑定(bind)到shared_from_this()
,这会将另一个shared_ptr
从绑定(bind)中馈入提示,从而增加共享的ref计数CCon
对象,并保证CCon
的生存期至少扩展到完成处理程序中。第三,请注意,我认为您的设计方法不是最佳解决方案。您不应该强制其他所有可能的传入连接都等待上一个连接经过某种身份验证过程。您要阻止接受者为新客户端执行其工作,直到先前的客户端与服务器进行对话为止。那是一个完全不必要的瓶颈。
我相信您正在这样做,因为您想在
::server
对象级别确定客户端是否正确通过了身份验证过程。相反,如果您真的需要服务器完全了解任何内容,则应该将其传递给CCon::Accept(...)
方法,该函数可以调用该函数或功能将其报告给服务器。复制asio模型,并将Accept
方法更改为AsyncAccept
,使其变为非阻塞状态,并在完成时提供回调,此时您可以检查其最终状态。