Boost Beast调用http::async_write触发“The handle is invalid”错误
问题背景
- 最初遇到
The file handle supplied is invalid报错,排查确认是socket提前关闭导致,修复该问题后又出现The handle is invalid错误 - 报错触发时,异步写回调的已传输字节参数
t值为4KB,表现为第一个4KB数据块写入完成后,回调或socket进入异常状态 - 若将异步接口
http::async_write替换为同步http::write接口,程序运行完全正常
问题对应实现代码如下:
void BeastResponse::write(http::response<http::file_body> responseFile) { std::cout << "BeastResponse while file write: " << this << std::endl; auto self = shared_from_this(); http::async_write(m_stream, responseFile, [self](beast::error_code ec, std::size_t t) { if (ec) { std::cout << "File Write Failed" << ": " << ec.message() << std::endl; std::cout << t << std::endl; } else { std::cout << t << std::endl; }; }); }
根因分析
核心诱因是待发送的responseFile对象生命周期没有覆盖整个异步写操作全程:
responseFile是write函数的形参,属于函数局部对象。http::async_write是异步非阻塞接口,发起IO操作后会立刻返回,不会等待发送完成。当write函数执行结束退出作用域时,responseFile会被立刻析构,其内部持有的file_body对应的文件句柄会被系统回收关闭。- 之所以能成功写入4KB后才报错,是因为异步操作发起时,初始的4KB数据块刚好被拷贝到socket发送缓冲区完成发送,后续操作需要继续从
responseFile持有的文件句柄读取剩余数据时,该对象已经被析构、文件句柄已经失效,因此触发The handle is invalid报错。 - 同步
http::write接口是阻塞执行的,会一直等到整个响应全部写入完成才会返回,这期间responseFile始终处于函数作用域内、没有被析构,内部文件句柄一直有效,因此不会触发该问题。
额外需要确认的点:代码中仅通过捕获self保证BeastResponse实例的生命周期,需要确认m_stream是BeastResponse的直接成员,不会在异步操作执行期间被提前销毁。
修复方案
将responseFile通过值捕获的方式传入异步回调,保证其生命周期和异步操作绑定,直到写操作完成后再析构。修复后代码示例:
void BeastResponse::write(http::response<http::file_body> responseFile) { std::cout << "BeastResponse while file write: " << this << std::endl; auto self = shared_from_this(); // 值捕获responseFile,保证其在异步操作完成前不会被析构 http::async_write(m_stream, responseFile, [self, responseFile](beast::error_code ec, std::size_t t) { if (ec) { std::cout << "File Write Failed" << ": " << ec.message() << std::endl; std::cout << t << std::endl; } else { std::cout << t << std::endl; } }); }
注:所有基于Asio/Beast的异步IO操作,都要求操作依赖的缓冲区、消息、流对象的生命周期必须覆盖整个异步操作周期,直到回调触发后再释放,否则就会出现悬空引用、资源提前释放的问题。
内容的提问来源于stack exchange,提问作者Suresh Ganapathy
相关产品推荐
相关产品推荐

