使用Boost::Asio读取缓冲区时出现乱码的原因及解决方法
问题:Boost.Asio async_read_until读取数据出现乱码
我有一个客户端程序负责读取文件并逐行将ASCII编码的数据发送至服务器,服务器需要统计发送的行数。使用boost::asio::async_read_until实现时,从缓冲区读取数据出现乱码(例如:Line: �68�)。
客户端代码片段
std::ifstream infile(argv[1]); std::string line; while (std::getline(infile, line)) { boost::asio::write(s, boost::asio::buffer(line + '\n')); std::cout << line << std::endl;
服务器读取函数代码片段
void handle_read(const boost::system::error_code& error, size_t bytes_trasferred, boost::asio::streambuf& buf) { if (!error) { if (!bytes_trasferred) { std::cout << "0 bytes trasferred\n"; return; } std::string data = boost::asio::buffer_cast<const char*>(buf.data()); std::istringstream is(data); std::string line; std::getline(is, line); std::cout << "Line: " << line << std::endl; } else std::cerr << "Error: " << error.message() << std::endl; } void do_read() { std::cout << "do_read\n"; auto self(shared_from_this()); boost::asio::streambuf buf; buf.prepare(1048576); std::cout << "ASYNC\n"; boost::asio::async_read_until(socket_, buf, "\n", boost::bind(&TcpConnection::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, boost::ref(buf))); }
已尝试的方案:
- 使用
self和this替代绑定对象,导致内存泄漏 - 在处理函数中获取数据后添加空终止符,问题仍未解决
解决方案
问题根源
- 局部缓冲区生命周期错误:
do_read中声明的局部boost::asio::streambuf buf,在异步操作发起后函数返回,缓冲区会被销毁,但回调handle_read仍会引用该已释放的内存,导致非法访问出现乱码。 - 缓冲区数据处理错误:直接通过
buffer_cast获取整个streambuf的数据,未考虑async_read_until返回的bytes_transferred是匹配分隔符的有效字节数,且streambuf数据并非空字符结尾,直接赋值给std::string会读取到缓冲区中未初始化的垃圾数据。
修复步骤
1. 修正缓冲区生命周期
将streambuf改为TcpConnection类的成员变量,确保异步操作完成前缓冲区不会被销毁:
class TcpConnection : public std::enable_shared_from_this<TcpConnection> { private: boost::asio::streambuf buf_; // 类成员缓冲区 // ... 其他成员变量 };
修改do_read函数,使用类成员缓冲区:
void do_read() { std::cout << "do_read\n"; auto self(shared_from_this()); buf_.prepare(1048576); std::cout << "ASYNC\n"; boost::asio::async_read_until(socket_, buf_, "\n", boost::bind(&TcpConnection::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, boost::ref(buf_))); }
2. 正确读取有效数据并清理缓冲区
在handle_read中,直接通过std::istream读取streambuf,并提交已消费的数据避免重复读取:
void handle_read(const boost::system::error_code& error, size_t bytes_transferred, boost::asio::streambuf& buf) { if (!error) { if (!bytes_transferred) { std::cout << "0 bytes transferred\n"; do_read(); return; } std::istream is(&buf); std::string line; std::getline(is, line); std::cout << "Line: " << line << std::endl; // 提交已读取的数据,清理缓冲区中已消费的部分 buf.consume(bytes_transferred); do_read(); // 继续监听下一行数据 } else { std::cerr << "Error: " << error.message() << std::endl; } }
3. 确保连接对象生命周期
使用shared_from_this()绑定回调,保证TcpConnection对象在异步操作完成前不会被销毁,避免内存泄漏或悬空引用。
内容的提问来源于stack exchange,提问作者CupaState
相关产品推荐
相关产品推荐

