提升asio io_context.run()分段错误

时间:2018-09-04 09:11:11

标签: boost asio

我正在尝试制作一个简单的服务器,该服务器可以在接收简短指令的情况下记住并操作某些变量。 我没有完成此服务器,并且正在尝试测试连接到服务器。 但是,当我尝试连接服务器时,会发生分段错误。 似乎发生在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;
}

1 个答案:

答案 0 :(得分:1)

有很多问题,仅指出一些:

  1. 声明

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

    这将创建一个从enable_shared_from_this继承的局部(堆栈)变量。使用shared_from_this将是Undefined Behaviour

    会话立即被破坏,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从未被初始化。再次:Undefined Behaviour。即使您进行了初始化,也必须管理生命周期。为什么不让它成为会话的成员,而不是动态分配(或像现在这样忘记分配)?

      

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

  4. client相同:data_从未初始化。景气。

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

    但是,由于data_proc很高兴地汇总了一个{strong> data_queue std::deque<>,因此显然 IS NOT POD < / strong>。更多Undefined Behaviour

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

    例如参见sending/receiving a struct in boost::asio

      

    注意,您现在在使用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版的内存。

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

    请介意,给我的印象是oper_data可能有类似的问题(尽管起初我以为operand__oper_num应该指向固定大小的缓冲区{ {1}})

总结:

您遥遥领先。开始要简单得多。使用C ++(oper_[],从不使用std::string / new,如果您想delete,则实际上使用std::make_shared)。

您会更加快乐。当您遇到困难时,可以随时提出一些更简单的问题,理想情况下,SSCCE应该是几十行左右。