Boost asio 获取“错误代码:125 操作已取消”

Boost asio getting `Error Code : 125 Operation cancelled`

我只是想用 acceptor socket 接受传入请求,当它进入 async_accept 时它抛出一个错误,我不确定是什么导致了错误。问题是我什至没有从客户端发送请求,但由于某种原因它仍然进入 async_accept 处理程序,这是导致错误

main.cpp

#include <iostream>


#include "server.hpp"
#include "connection.hpp"

class Connection;

int main(){
    using Server_ = Server<Connection>;
    auto server = std::make_unique<Server_>(8989);

    server->start();
}

server.hpp

#pragma once

#include <thread>

#include <boost/asio.hpp>

template <typename Connection> 
class Server{
    using shared_connection = std::shared_ptr<Connection>; 

    private:
    unsigned short port_;
    std::thread io_thread_;
    boost::asio::io_context ioc_;
    boost::asio::io_context::work work_;
    boost::asio::ip::tcp::endpoint endpoint_;
    boost::asio::ip::tcp::acceptor acceptor_;

    void handle_new_request(shared_connection connection, const system::error_code &ec){
        if(!ec){
            connection->start_operation();
        }else{
            error::print(ec);
            return;
        }
    }

    public:
    explicit Server(unsigned short port)
    : acceptor_(ioc_)
    , work_(ioc_)
    , port_(port)
    , endpoint_(asio::ip::tcp::v4(), port) { 
        io_thread_ = std::move(std::thread([&]{ ioc_.run(); }));
        io_thread_.join();
    }
    
    ~Server() { 
        if(acceptor_.is_open()) 
            acceptor_.close(); 
        io_thread_.join();
    }

    void start(){
        using namespace asio::ip;

        system::error_code ec;

        // creates an actual operating system socket
        acceptor_.open(endpoint_.protocol(),ec); 
        acceptor_.set_option(tcp::acceptor::reuse_address(true),ec);
        // binds to the endpoint
        acceptor_.bind(endpoint_,ec); 

        if(!ec){
            std::cout << "Listening for requests from port " << port_ << std::endl;
            acceptor_.listen();
        }else{
            error::print(ec);
            return;
        }

        shared_connection connection = std::make_shared<Connection>(ioc_);
        acceptor_.async_accept(connection->sock_,[=](system::error_code ec){
            handle_new_request(connection,ec);
        });
    }

};

connection.hpp

#pragma once

#include <memory>

#include <boost/asio.hpp>

class Connection : public std::enable_shared_from_this<Connection> {
    using shared_connection = std::shared_ptr<Connection>;
        std::vector<char> buffer_space_;
    private:
        boost::asio::mutable_buffers_1 buffer_;
        boost::asio::io_context& ioc_;
    public:
        boost::asio::ip::tcp::socket sock_;

        explicit Connection(boost::asio::io_context &context,const int &size = 1024)
            : ioc_(context)
            , sock_(context)
            , buffer_space_(size) 
            , buffer_(buffer_space_.data(),size){}
        
        ~Connection(){ if(sock_.is_open()) sock_.close(); }
        void start_operation(){
            if(sock_.is_open()){
                sock_.async_read_some(buffer_,[me = shared_from_this()](const system::error_code &ec, std::size_t bytes){
                    if(!ec){
                        for(int i=0;i<bytes;++i)
                            std::cout << me->buffer_space_[i];
                        std::cout << std::endl;
                        me->start_operation();
                    }else{
                        error::print(ec);
                        return;
                    }
                });
            }
        }
};

error.hpp

#pragma once

#include <iostream>

#include <boost/system/error_code.hpp>

namespace error {
    inline void print(const boost::system::error_code &ec){
        std::cerr << "Error Code : " << ec.value() << ", Message : " << ec.message() << std::endl;
    }
}

如有任何帮助,我们将不胜感激。谢谢!

错误在于析构函数中的 io_thread_.run() 会在那时销毁套接字对象

int main(){
    using Server_ = Server<Connection>;
    auto server = std::make_unique<Server_>(8989);

    server->start();
}

server 将调用删除器,它在退出 main 时销毁 Server_。您想在退出 main 之前加入任何线程或等待服务器关闭。

在你的情况下,你会加入 iothread,就像你尝试做的那样。

但是,您在服务器构造函数中执行此操作:

explicit Server(unsigned short port)
    : acceptor_(ioc_)
    , work_(ioc_)
    , port_(port)
    , endpoint_(asio::ip::tcp::v4(), port)
{
    io_thread_ = std::move(std::thread([&] { ioc_.run(); }));
    io_thread_.join();
}

I can't really figure out how this would not hang indefinitely due to the work_. On tangentially related observation is that work_ is NOT initilaized before ioc_ (or thread_) because initialization happens in order of member declaration instead of order in which the initializers appear. You will want to fix the declaration order regardless:

boost::asio::io_context ioc_;
boost::asio::ip::tcp::acceptor acceptor_;
boost::asio::io_context::work work_;
unsigned short port_;
boost::asio::ip::tcp::endpoint endpoint_;
std::thread io_thread_;

Similarly in Connection:

  private:
    boost::asio::io_context& ioc_;

  public:
    boost::asio::ip::tcp::socket sock_;

  private:
    std::vector<char> buffer_space_;
    boost::asio::mutable_buffers_1 buffer_;

固定演示

Live On Coliru

#include <iostream>

#include <boost/system/error_code.hpp>

namespace error {
    inline void print(const boost::system::error_code& ec)
    {
        std::cerr << "Error Code : " << ec.value()
                  << ", Message : " << ec.message() << std::endl;
    }
}

#include <memory>

#include <boost/asio.hpp>

class Connection : public std::enable_shared_from_this<Connection> {
    using shared_connection = std::shared_ptr<Connection>;

  private:
    boost::asio::io_context& ioc_;

  public:
    boost::asio::ip::tcp::socket sock_;

  private:
    std::vector<char> buffer_space_;
    boost::asio::mutable_buffers_1 buffer_;

  public:
    explicit Connection(
        boost::asio::io_context& context, const int& size = 1024)
        : ioc_(context)
        , sock_(context)
        , buffer_space_(size)
        , buffer_(buffer_space_.data(), size)
    {
    }

    void start_operation()
    {
        if (sock_.is_open()) {
            sock_.async_read_some(buffer_,
                [me = shared_from_this()](
                    const boost::system::error_code& ec, std::size_t bytes) {
                    if (!ec) {
                        for (size_t i = 0; i < bytes; ++i) {
                            std::cout << me->buffer_space_[i];
                        }
                        std::cout << std::endl;
                        me->start_operation();
                    } else {
                        error::print(ec);
                        return;
                    }
                });
        }
    }
};
#include <thread>

#include <boost/asio.hpp>

template <typename Connection> class Server {
    using shared_connection = std::shared_ptr<Connection>;

  private:
    boost::asio::io_context ioc_;
    boost::asio::ip::tcp::acceptor acceptor_;
    boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
        work_ {ioc_.get_executor()};
    uint16_t port_;
    boost::asio::ip::tcp::endpoint endpoint_;
    std::thread io_thread_;

    void handle_new_request(
        shared_connection connection, const boost::system::error_code& ec)
    {
        if (!ec) {
            connection->start_operation();
        } else {
            error::print(ec);
            return;
        }
    }

  public:
    explicit Server(uint16_t port)
        : acceptor_(ioc_)
        , port_(port)
        , endpoint_(boost::asio::ip::tcp::v4(), port)
        , io_thread_([&] { ioc_.run(); })
    { ; }

    ~Server()
    {
        if (acceptor_.is_open()) {
            boost::system::error_code ec;
            acceptor_.cancel(ec);
            //acceptor_.close(ec);
        }
        work_.reset();
        io_thread_.join();
    }

    void start()
    {
        using boost::asio::ip::tcp;

        boost::system::error_code ec;

        // creates an actual operating system socket
        acceptor_.open(endpoint_.protocol(), ec);
        acceptor_.set_option(tcp::acceptor::reuse_address(true), ec);
        // binds to the endpoint
        acceptor_.bind(endpoint_, ec);

        if (!ec) {
            std::cout << "Listening for requests from port " << port_
                      << std::endl;
            acceptor_.listen();
        } else {
            error::print(ec);
            return;
        }

        shared_connection connection = std::make_shared<Connection>(ioc_);
        acceptor_.async_accept(
            connection->sock_, [=, this](boost::system::error_code ec) {
                handle_new_request(connection, ec);
            });
    }
};

#include <iostream>

//#include "server.hpp"
//#include "connection.hpp"
using namespace std::chrono_literals;

class Connection;

int main()
{
    using Server_ = Server<Connection>;
    auto server = std::make_unique<Server_>(8989);

    server->start();

    std::this_thread::sleep_for(4s);
    // destructor joins
}

这将为第一个连接的客户端提供 4 秒,并在所有连接完成后立即关闭。