boost asio io_context.run () ошибка сегментации - PullRequest
0 голосов
/ 04 сентября 2018

Я пытаюсь сделать простой сервер, который запоминает и управляет некоторыми переменными, получая короткие инструкции. Я не завершил этот сервер, и я пытаюсь проверить подключение к серверу. Но когда я пытаюсь подключиться к серверу, возникает ошибка сегментации. Похоже, что это происходит в функции 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 голосов
/ 04 сентября 2018

Есть много проблем, просто указав на несколько:

  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 никогда не инициализируется. Снова: Неопределенное поведение . Даже если бы вы его инициализировали, вам бы пришлось управлять временем жизни; почему бы не сделать его членом сеанса, вместо того, чтобы динамически распределять (или забывать, как у вас сейчас)?

    Внимание : Нет, вы не можете размещать в стеке внутри start(), даже если вы захватываете его в лямбду, потому что асинхронные операции не завершатся до выхода start().

  4. То же самое в client: data_ никогда не инициализируется. Boom.

  5. Даже если он был правильно выделен, использование его как asio::buffer() обрабатывает его как POD.

    Так как, однако, data_proc счастливо агрегирует data_queue, что составляет std::deque<>, это, очевидно, НЕ СОДЕРЖИТ . Подробнее Неопределенное поведение .

    Что вам, вероятно, нужно, это сериализовать ваши структуры данных, вместо того, чтобы надеяться, что копирование некоторых байтов памяти будет волшебным образом «работать». Не будет!

    Смотри, например, отправка / получение структуры в 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.
    }
    

    Так много всего ...

    • использовать std::getline, а не std::istream::getline
    • нить должна быть присоединена (https://en.cppreference.com/w/cpp/thread/thread/~thread)
    • если все, что вы делаете, это блок для ввода, зачем нужен поток?

        io_context.run(); // replaces all of the above
      
  7. data_store также не POD, но это также живая утечка памяти. Вся память new никогда не освобождается.

    Обратите внимание, что, как это написано, структура может выглядеть как POD, но логически это не так (Правило Три). По сути, вы написали это на C, а не на C ++. Это исключает все абстракции, которые есть в C ++, и теперь компилятор не может сказать, что структура ссылается на не принадлежащие ей ресурсы.

    Имейте в виду, это создает у меня впечатление, что oper_data может иметь аналогичные проблемы (хотя сначала я предполагал, что operand_ и _oper_num должны указывать внутри буфера фиксированного размера oper_[])

Подведение итогов:

Ты далеко впереди себя. Начать гораздо проще. Используйте C ++ (std::string, никогда не используйте new / delete, на самом деле используйте std::make_shared, если хотите enable_shared_from_this).

Вы будете намного счастливее. Не стесняйтесь возвращаться с более простыми вопросами, когда вы застряли, в идеале SSCCE будет состоять из (нескольких) дюжин или около того строк.

...