首页 > 解决方案 > boost asio io_context.run() 分段错误

问题描述

我正在尝试制作简单的服务器来记住并操作一些变量并接收简短的指令。我没有完成此服务器,我正在尝试测试连接到服务器。但是当我尝试连接服务器时,会发生分段错误。似乎发生在 io_context.run() 函数中。尽管阅读了 asio 的参考页,但我不知道此错误的确切原因。请帮我..

我认为您不必阅读数据代码(data.hpp)。这是服务器代码。

//server.cpp
#include <iostream>
#include "network/sc_network.hpp"

int main(int argc, char *argv[])
{
    try
    {
        if(argc != 2)
        {
            std::cerr << "Usage: server <port>\n";
            return 1;
        }

        boost::asio::io_context io_context;

        tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[1]));
        server server(io_context, endpoint);

        io_context.run();

    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}

这是客户端代码。

//client.cpp
#include <iostream>
#include <thread>
#include <cstdlib>
#include <boost/asio.hpp>
#include "network/data/data.hpp"

using boost::asio::ip::tcp;

class client{
private:
    boost::asio::io_context& io_context_;
    tcp::socket socket_;
    oper_data *data_;

    void do_connect(const tcp::resolver::results_type& endpoints)
    {
        boost::asio::async_connect(socket_, endpoints,
            [this](boost::system::error_code ec, tcp::endpoint)
            {
                if(!ec)
                {
                    boost::asio::async_read(socket_,
                        boost::asio::buffer(data_, sizeof(oper_data)),
                        [this](boost::system::error_code ec, std::size_t)
                        {
                            if(!ec)
                            {
                                boost::asio::async_write(socket_,
                                    boost::asio::buffer(data_,sizeof(oper_data)),
                                    [this](boost::system::error_code ec, std::size_t)
                                    {

                                    });
                            }
                            else
                            {
                                socket_.close();    
                            }
                        });
                }

                else
                {
                    socket_.close();
                }
            });
    }
public:
    client(boost::asio::io_context& io_context,
        const tcp::resolver::results_type& endpoints)
        : io_context_(io_context),
        socket_(io_context) 
    {
        do_connect(endpoints);
    }
    void write(const oper_data& data)
    {
        boost::asio::post(io_context_,
        [this, data]()
        {

        });
    }
};

int main(int argc, char *argv[])
{
    try
    {
        if(argc != 3)
        {
            std::cerr << "Usage: client <host> <port>\n";
            return 1;

        }
        boost::asio::io_context io_context;

        tcp::resolver resolver(io_context);
        auto endpoints = resolver.resolve(argv[1], argv[2]);
        client c(io_context, endpoints);

        std::thread t([&io_context](){ io_context.run(); });

        char line[128];

        while (std::cin.getline(line, 128))
        {
            oper_data data;
            //processing the line with deviding in 3 words.
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

这是 sc_network.hpp

//sc_network.hpp
#include <boost/asio.hpp>
#include <memory>
#include <utility>
#include "data/data.hpp"

using boost::asio::ip::tcp;

class session
    : public std::enable_shared_from_this<session>
{
private:
    tcp::socket socket_;
    data_proc data_proc_;
public:
    session(tcp::socket socket)
        : socket_(std::move(socket)){}

    void start()
    {
        oper_data *input_data;
        boost::asio::async_read(socket_,
            boost::asio::buffer(input_data, sizeof(oper_data)),
            [this, input_data](boost::system::error_code ec, std::size_t)
            {
                if(!ec)
                {
                    data_proc_.set_data(*input_data);
                    data_proc_.oper_process();
                    start();
                }
                else
                {
                    return;
                }
            });
    }
};

class server
{
private:
    tcp::acceptor acceptor_;

    void do_accept()
    {
        acceptor_.async_accept(
            [this](boost::system::error_code ec, tcp::socket socket)
            {
                if(!ec)
                {
                    session ex_session(std::move(socket));
                }

                do_accept();
            });
    }
public:
    server(boost::asio::io_context& io_context,
        const tcp::endpoint& endpoint)
        : acceptor_(io_context, endpoint)
    {
        do_accept();
    }

};

这是data.hpp。

//data.hpp
#include <deque>
#include <cstring>
#include "favdew_utility.hpp"

#define max_oper_size 5
#define max_oper_buf max_oper_size + 1

struct oper_data {
    char oper_[max_oper_buf] = "\0";
    char *operand_;
    char *oper_num_;
};

typedef struct oper_data oper_data;

class data_store {
private:
    char *var_name_;
    char *var_value_;
public:
    data_store()
        : var_name_(NULL), var_value_(NULL) {}

    data_store(const char *var_name, const char *var_value)
    {
        std::size_t var_name_size = strlen(var_name) + 1;
        var_name_ = new char[var_name_size];
        strncpy(var_name_, var_name, strlen(var_name));

        std::size_t var_value_size = strlen(var_value) + 1;
        var_value_ = new char[var_value_size];
        strncpy(var_value_, var_value, strlen(var_value));
    }
    char *var_name() { return var_name_; }
    char *var_value() { return var_value_; }
    void set_value(const char *var_value) {
        var_value_ = new char[strlen(var_value) + 1];
        strncpy(var_value_, var_value, strlen(var_value));
    }
};

typedef std::deque<data_store> data_queue;

class data_proc {
private:
    oper_data data_;
    data_queue proc_queue;

    void var()
    {
        if (data_store *var = this->get_var(data_.operand_)) {
            var->set_value(data_.oper_num_);
        }
        else {
            data_store input_data(data_.operand_, data_.oper_num_);
            this->proc_queue.push_back(input_data);
        }
    }



    bool sum()
    {
        data_store *var = this->get_var(data_.operand_);
        if ( (var) && isNumber(var->var_value()))
        {
            const int input_data = std::atoi(var->var_value()) +
                std::atoi(this->data_.oper_num_);
            var->set_value(std::to_string(input_data).c_str());
            return true;
        }
        else
            return false;
    }

    bool dif()
    {
        data_store *var = this->get_var(data_.operand_);
        if ((var) && isNumber(var->var_value()))
        {
            const int input_data = std::atoi(var->var_value()) -
                std::atoi(this->data_.oper_num_);
            var->set_value(std::to_string(input_data).c_str());
            return true;
        }
        else
            return false;
    }
public:
    data_proc()
    {
        oper_data input_data;

        //<input_data.oper_> is already initialized with "\0"
        std::memset(input_data.operand_, 0, sizeof(char *));
        std::memset(input_data.oper_num_, 0, sizeof(char *));
    }

    data_proc(const char *oper, const char *operand, const char *oper_num)
    {
        strncpy(data_.oper_, oper, max_oper_size);

        std::size_t operand_size = strlen(operand) + 1;
        data_.operand_ = new char[operand_size];
        strncpy(data_.operand_, operand, strlen(operand));

        std::size_t oper_num_size = strlen(oper_num) + 1;
        data_.oper_num_ = new char[oper_num_size];
        strncpy(data_.oper_num_, oper_num, strlen(oper_num));

    }

    inline void set_data(oper_data data)
    {
        this->data_ = data;
    }

    void set_data(const char *oper, const char *operand, const char *oper_num)
    {
        strncpy(data_.oper_, oper, max_oper_size);

        std::size_t operand_size = strlen(operand) + 1;
        data_.operand_ = new char[operand_size];
        strncpy(data_.operand_, operand, strlen(operand));

        std::size_t oper_num_size = strlen(oper_num) + 1;
        data_.oper_num_ = new char[oper_num_size];
        strncpy(data_.oper_num_, oper_num, strlen(oper_num));
    }

    data_store *get_var(const char *var_name)
    {
        const std::size_t queue_size = this->proc_queue.size();

        for (std::size_t i=0; i < queue_size; i++) {
            if (!strcmp(this->proc_queue[i].var_name(), var_name)) {
                return &proc_queue[i];
            }
        }
        return NULL;
    }


    bool oper_process()
    {
        const char *oper = this->data_.oper_;
        if (!strcmp(oper, "var")) {
            var();
            return true;
        }
        else if (!strcmp(oper, "sum")) {
            sum();
            return true;
        }
        else if (!strcmp(oper, "dif")) {
            dif();
            return true;
        }
        else {
            return false;
        }
    }
};

这是 favdew_utility.hpp

#include <string>
#include <cstdlib>

bool isNumber(const char *str)
{
    std::size_t length = strlen(str);

    for (std::size_t i = 0; i < length; i++)
    {
        if (!('0' < str[i] && str[i] < '9'))
            return false;

        continue;
    }
    return true;
}

bool isEmpty(void *buffer)
{
    if (!buffer || *(char *)buffer == '\0')
        return true;
    else
        return false;
}

标签: boostasio

解决方案


问题很多,只提几点:

  1. 宣言

    session ex_session(std::move(socket));
    

    这将创建一个继承自 的本地(堆栈)变量enable_shared_from_this。使用shared_from_this将是未定义的行为

    会话立即被破坏并且start()似乎从未被调用

  2. 如果session::start() 调用,它将失败,因为它在不保护实例生命周期的session情况下启动异步操作:

    boost::asio::async_read(socket_,
        boost::asio::buffer(input_data, sizeof(oper_data)),
        [this, input_data](boost::system::error_code ec, std::size_t) { ....
    

    至少您需要捕获指向会话的共享指针:

      auto self = shared_from_this();
      boost::asio::async_read(socket_,
        boost::asio::buffer(input_data, sizeof(oper_data)),
        [this, self, input_data](boost::system::error_code ec, std::size_t)
    
  3. 更糟糕的input_data是,从未初始化。再次: 未定义的行为。即使你确实初始化了它,你也必须管理生命周期;为什么不让它成为会话的成员,而不是像现在这样动态分配(或忘记分配)?

    注意:不,即使在 lambda 中捕获它,也不能在内部进行堆栈分配,因为异步操作在退出start()之前不会完成。start()

  4. 同样在client:data_永远不会初始化。繁荣。

  5. 即使您正确分配了它,使用它也asio::buffer()将其视为 POD。

    但是,因为很data_proc高兴地聚合 adata_queuestd::deque<>显然是不是 POD。更多 未定义的行为

    您可能需要的是序列化您的数据结构,而不是希望复制一些内存字节会神奇地“工作”。不会的!

    参见例如在 boost::asio 中发送/接收结构

    注意当您在 is 时,使用 C++ 而不是 C?所有原始指针和char*复杂性都是您不需要的,它正在处理您的数十支脚枪或末端或绳索,您会更加伤害自己。

  6. client.cpp你有:

    std::thread t([&io_context](){ io_context.run(); });
    
    char line[128];
    
    while (std::cin.getline(line, 128))
    {
        oper_data data;
        //processing the line with deviding in 3 words.
    }
    

    东西太多了...

  7. data_store也不是 POD,但它也是一个活的内存泄漏。所有的new-ed 内存永远不会被释放。

    请注意,按照它的编写方式,该结构可能看起来是 POD,但在逻辑上它不是(三规则)。基本上,你是用 C 而不是 C++ 编写的。这放弃了 C++ 的所有抽象,现在编译器无法判断该结构是指非拥有的资源。

    请注意,这给我的印象oper_data可能有类似的问题(尽管起初我假设operand_并且_oper_num应该指向固定大小的缓冲区内oper_[]

总结:

你已经遥遥领先了。开始简单得多。使用 C++ ( std::string,永远不要使用new/ ,如果你想delete实际使用)。std::make_sharedenable_shared_from_this

你会快乐很多。当您遇到困难时,请随时提出更简单的问题,理想情况下,SSCCE 将是(几)行左右。


推荐阅读