我通过boost.beast写了一个异步websocket。但是当我尝试运行它时,无法连接它。
服务器代码如下。当我尝试连接Websocket服务器时,我的镶边显示连接状态。 当我通过VS2017进行调试时,它永远不会在run()的lambda表达式中运行。
iListener::iListener( boost::asio::io_context& ioc,boost::asio::ip::tcp::endpoint endpoint) : acceptor_(ioc), socket_(ioc) {
boost::system::error_code ec;
std::cout<<"iListener"<<std::endl;
// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
// fail(ec, "open");
return;
}
// Allow address reuse
acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
if (ec) {
// fail(ec, "set_option");
return;
}
// Bind to the server address
acceptor_.bind(endpoint, ec);
if (ec) {
// fail(ec, "bind");
return;
}
// Start listening for connections
acceptor_.listen(
boost::asio::socket_base::max_listen_connections, ec);
if (ec) {
std::cout << ec.message() << " listen" << std::endl;
// fail(ec, "listen");
return;
}
}
iListener::~iListener() {
}
void iListener::run() {
if (!acceptor_.is_open()) {
return;
}
std::cout<<"iListener run"<<std::endl;
while (true) {
acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) {
std::cout << "now run listener" << std::endl;
if (ec1) {
std::cout<<ec1.message()<<" accept"<<std::endl;
// fail(ec, "accept");
} else {
// Create the session and run it
std::make_shared<NormalSession>(std::move(socket_))->run();
}
});
}
}
void iListener::initListener(const std::string &addressStr, unsigned short port, int threads){
auto const address = boost::asio::ip::make_address(addressStr);
boost::asio::io_context ioc{threads};
std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
std::vector<std::thread> v;
v.reserve(threads - 1);
for(auto i = threads - 1; i > 0; --i)
v.emplace_back(
[&ioc]
{
ioc.run();
});
ioc.run();
}
当我尝试在Chrome控制台上进行连接时。 连接时间很长,然后显示失败。
所以我改回去,以升压为例。
void iListener::run() {
if (!acceptor_.is_open()) {
return;
}
// std::cout<<"iListener run"<<std::endl;
// while (true) {
// acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) {
//std::cout << "now run listener" << std::endl;
// if (ec1) {
// std::cout<<ec1.message()<<" accept"<<std::endl;
// // fail(ec, "accept");
// } else {
// // Create the session and run it
// std::make_shared<NormalSession>(std::move(socket_))->run();
// }
// });
// }
do_accept();
}
void iListener::do_accept() {
acceptor_.async_accept(
socket_,
std::bind(
&iListener::on_accept,
shared_from_this(),
std::placeholders::_1));
}
void iListener::on_accept(boost::system::error_code ec) {
if (ec)
{
std::cout << ec.message() << " on_accept" << std::endl;
}
else
{
// Create the session and run it
std::make_shared<NormalSession>(std::move(socket_))->run();
}
// Accept another connection
do_accept();
}
我有两个问题:
1。为什么我使用lambda,它将是SOF,但示例不会。 2.当我使用while()时,它不起作用,为什么? lambda表达式和std :: bind()之间有什么区别?
还有另一个问题,下面的两个代码块有什么区别?
void iListener::do_accept() {
acceptor_.async_accept(
socket_,
[&](boost::system::error_code ec1) mutable {
on_accept(ec1);
}
}
void iListener::do_accept() {
acceptor_.async_accept(
socket_,
std::bind(
&iListener::on_accept,
shared_from_this(),
std::placeholders::_1));
}
当我使用前一个时,它返回error_code 995。
答案 0 :(得分:1)
编辑
bind
和lambda
有什么区别?在前者中,您可以延长iListener
实例的寿命,而在后者中则不需要。
我们需要从这一行开始:
std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
// [a]
如果您不延长iListener
中run
的生存期,则在[a]行iListener
中的实例将被销毁。
作为绑定的参数之一,您正在传递shared_from_this
,它会从shared_ptr
指针创建this
,因此bind
返回的函子对象将智能指针保持为{{1 }}实例可以延长其寿命。
iListener
的生存期未延长,您调用iListener
传递lambda,而不会增加当前对象的引用计数器,即,调用了async_accept
的对象。因此do_accept
立即返回,async_accept
结束,最后do_accept
也结束,并且run
创建的对象被删除。
您需要通过将std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})
的值传递到lambda中来更新引用计数器:
shared_ptr
由void iListener::do_accept() {
auto sp = shared_from_this();
acceptor_.async_accept(
socket_,
[&,sp](boost::system::error_code ec1) mutable
{
on_accept(ec1);
}
}
调用async_accept
发起的任务的处理程序(在您的情况下为lambda主体),您看不到此执行,因为您的代码挂在此行上:
io_context::run
这将创建std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
实例并调用iListener
,该实例包含无限循环且永远不会结束:
run
因此您无法到达以while (true) { // INFINITE LOOP
acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) {
std::cout << "now run listener" << std::endl;
if (ec1) {
std::cout<<ec1.message()<<" accept"<<std::endl;
// fail(ec, "accept");
} else {
// Create the session and run it
std::make_shared<NormalSession>(std::move(socket_))->run();
}
});
}
开头的可调用处理程序的行。
修复:启动io_context::run
之前,您可以启动另一个执行io_context::run
的线程。
访问Boost Asio examples,以了解如何使用iListener::run
。常见的方法是从其处理程序中调用async_accept
,但是如果要执行此操作,则async_accept
应该从iListener
派生,以延长传递到处理程序中的寿命。
另一个问题是enable_shared_from_this
数据成员。我假设您想在每个会话中容纳一个套接字,但是现在您的代码无法正确处理它。您只有socket_
的一个实例,如果建立了新的连接,该实例将被移到socket_
中。因此,当第二次调用NormalSession
时,您正在传递INVALID套接字。它行不通。这会导致不确定的行为。
执行以下行后
async_accept
您可能会忘记std::make_shared<NormalSession>(std::move(socket_))->run();
。
socket_
已超载,您可以将带有handler的版本与新接受的套接字一起使用。
但是,如果您希望保持当前版本为async_accept
,则需要确保每次调用socket
时都使用唯一的套接字实例。