boost - 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;
}
解决方案
问题很多,只提几点:
宣言
session ex_session(std::move(socket));
这将创建一个继承自 的本地(堆栈)变量
enable_shared_from_this
。使用shared_from_this
将是未定义的行为会话立即被破坏并且
start()
似乎从未被调用如果
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)
更糟糕的
input_data
是,从未初始化。再次: 未定义的行为。即使你确实初始化了它,你也必须管理生命周期;为什么不让它成为会话的成员,而不是像现在这样动态分配(或忘记分配)?注意:不,即使在 lambda 中捕获它,也不能在内部进行堆栈分配,因为异步操作在退出
start()
之前不会完成。start()
同样在
client
:data_
永远不会初始化。繁荣。即使您正确分配了它,使用它也
asio::buffer()
将其视为 POD。但是,因为很
data_proc
高兴地聚合 adata_queue
这std::deque<>
显然是不是 POD。更多 未定义的行为。您可能需要的是序列化您的数据结构,而不是希望复制一些内存字节会神奇地“工作”。不会的!
注意当您在 is 时,使用 C++ 而不是 C?所有原始指针和
char*
复杂性都是您不需要的,它正在处理您的数十支脚枪或末端或绳索,您会更加伤害自己。在
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
- 使用
data_store
也不是 POD,但它也是一个活的内存泄漏。所有的new
-ed 内存永远不会被释放。请注意,按照它的编写方式,该结构可能看起来是 POD,但在逻辑上它不是(三规则)。基本上,你是用 C 而不是 C++ 编写的。这放弃了 C++ 的所有抽象,现在编译器无法判断该结构是指非拥有的资源。
请注意,这给我的印象
oper_data
可能有类似的问题(尽管起初我假设operand_
并且_oper_num
应该指向固定大小的缓冲区内oper_[]
)
总结:
你已经遥遥领先了。开始简单得多。使用 C++ ( std::string
,永远不要使用new
/ ,如果你想delete
实际使用)。std::make_shared
enable_shared_from_this
你会快乐很多。当您遇到困难时,请随时提出更简单的问题,理想情况下,SSCCE 将是(几)行左右。
推荐阅读
- html - 更改导航以向左展开
- python - 如何在 Python 3 中等待字符输入时输出?
- java - FileProvider - java.lang.IllegalArgumentException:找不到配置的根目录,其中包含
- linux - 如何快速转储 Postgres 数据库
- c# - Roslyn - 使用 CSharpCompilation 编译程序集以在 CSharpCompilation 编译的另一个程序中使用
- sql-server - SSRS 无法在报表生成器中连接到 SQL Server
- ruby-on-rails - Ruby-Rails 网络 ldap 搜索
- java - 无法使用 MSAL4J 用户名-密码流验证 MS Graph
- r - R:在 data.frame 中对数值数据进行排名
- c# - 如何跨越
和 stackalloc 创建一个临时的小列表