Boost Asio异步发送数据(async_write)崩溃问题记录

背景

服务端与客户端之间的网络通信(使用Boost Asio库异步编程模式实现),客户端会向服务端请求数据。

在刚开始的测试中,是没有出现问题的。后来有一次测试时,服务端查询完数据后,向客户端发送时总是崩溃。

通过gdb调试,可以发现是在调用到异步发送函数(boost::asio::async_write)后崩溃的。

打印的栈信息如下:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000000056625c in boost::asio::detail::reactive_socket_service_base::start_op (this=0x2e33313a30313a5f, impl=..., op_type=1, op=0x7fc5b4000af0, is_continuation=false, 
    is_non_blocking=true, noop=false) at /usr/local/include/boost/asio/detail/impl/reactive_socket_service_base.ipp:219
219	  reactor_.post_immediate_completion(op, is_continuation);
[Current thread is 1 (Thread 0x7fc5f7fff700 (LWP 128087))]
(gdb) bt
#0  0x000000000056625c in boost::asio::detail::reactive_socket_service_base::start_op (this=0x2e33313a30313a5f, impl=..., op_type=1, op=0x7fc5b4000af0, is_continuation=false, 
    is_non_blocking=true, noop=false) at /usr/local/include/boost/asio/detail/impl/reactive_socket_service_base.ipp:219
#1  0x000000000056bed5 in boost::asio::detail::reactive_socket_service_base::async_send<boost::asio::mutable_buffers_1, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> > > (this=0x2e33313a30313a5f, impl=..., buffers=..., flags=0, handler=...)
    at /usr/local/include/boost/asio/detail/reactive_socket_service_base.hpp:216
#2  0x000000000056b20f in boost::asio::stream_socket_service<boost::asio::ip::tcp>::async_send<boost::asio::mutable_buffers_1, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> > >(boost::asio::detail::reactive_socket_service<boost::asio::ip::tcp>::implementation_type&, boost::asio::mutable_buffers_1 const&, int, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> >&&) (this=0x2e33313a30313a37, impl=..., buffers=..., flags=0, handler=<unknown type in /a.out, CU 0x5ed5a4, DIE 0x6317d1>)
    at /usr/local/include/boost/asio/stream_socket_service.hpp:334
#3  0x000000000056a708 in boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >::async_write_some<boost::asio::mutable_buffers_1, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> > >(boost::asio::mutable_buffers_1 const&, boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> >&&) (
    this=0x2473c38, buffers=..., handler=<unknown type in /a.out, CU 0x5ed5a4, DIE 0x62fcbd>) at /usr/local/include/boost/asio/basic_stream_socket.hpp:732
#4  0x0000000000569831 in boost::asio::detail::write_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> >::operator() (this=0x7fc5f7ffde20, ec=..., bytes_transferred=0, start=1) at /usr/local/include/boost/asio/impl/write.hpp:258
#5  0x0000000000568263 in boost::asio::async_write<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running> >(boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >&, boost::asio::mutable_buffers_1 const&, boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, boost::_bi::bind_t<void, boost::_mfi::mf2<void, TcpSession, boost::system::error_code const&, unsigned long>, boost::_bi::list3<boost::_bi::value<boost::shared_ptr<TcpSession> >, boost::arg<1>, boost::arg<2> > >, boost::asio::detail::is_continuation_if_running>&&) (s=..., buffers=..., handler=<unknown type in /a.out, CU 0x5ed5a4, DIE 0x62aa66>)
    at /usr/local/include/boost/asio/impl/write.hpp:621
#6  0x000000000056306b in TcpSession::StartSend (this=0x246ec20, pData=0x7fc5a000f158 "\314F", nDataSize=18128) at SourceFiles/Common/ClientMgrBase.cpp:259
分析

  1. 数据缓冲区有效性

在Boost Asio异步编程中,最常见的错误莫过于数据缓冲区的失效。这个在编程初期已经进行了严格的检查。

在服务端的实现中,使用了TcpSession类来管理socket和它相关的read/write缓冲,它继承自boost::enable_shared_from_this<TcpSession>,并在bind仿函数中使用智能指针,所以这块应该是没有问题的。

  1. socket类非线程安全

另外一个,是socket的非线程安全性。对于同一个socket,不能在一个线程读的同时,在另一个线程中执行写操作。

其实,在数据量比较小时(比如小于65535),多线程操作socket一般也不会有什么问题。

  1. 结合测试情况和debug

结合测试具体情况,在先前的测试中,并未出现问题,测试条件的改变在于读取的数据量变大。

而在boost::asio::async_write实现中,使用定长的数组作为发送数据缓冲区。在发送函数到来的时候,先把数据拷贝到这个缓冲区,再调用发送函数。

一个现实的情况是,当数据量太大时,会超过发送数据缓冲区,这样就会导致内存使用异常。

虽然在拷贝的时候没有崩溃,但在真正发送数据时,发现buff违例,导致崩溃。接收数据时也会存在类似情况。

所以最终定准为数据缓冲区的使用问题,最简单的方法是修改为string,这样就由string类的asign方法来承载待发送的数据,不会出现超出范围的问题(内存允许)。

小结

在实际测试中,当我修改客户端的recv函数的数据缓冲区小到不能容纳所接收的数据时,也会出现崩溃的现象,但打印的栈信息与上述不同。

可见,在数据缓冲区的处理方面,我的程序还有不少需要改进的地方。由于不能预知数据的最大大小,所以应该考虑动态分配内存的方式,比如std::vector类型。

总之,当Boost Asio库由于异常崩溃时,不要因为看不懂栈信息而着急上火,还是要结合相关信息和已有知识储备,由浅入深,逐步排查定位。

参考资料

boost::asio::socket thread safety

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Boost.Asio 是一个功能强大的 C++ 库,提供了对网络和底层 I/O 操作的跨平台支持。它可以用于实现 TCP 异步通信。 要使用 Boost.Asio 进行 TCP 异步通信,首先需要创建一个 io_context 对象,它负责处理底层的 I/O 操作。然后,创建一个 tcp::socket 对象,用于建立和管理 TCP 连接。接下来,使用异步操作来进行连接、读取和写入数据。 下面是一个简单的示例代码,展示了如何使用 Boost.Asio 进行 TCP 异步通信: ```cpp #include <iostream> #include <boost/asio.hpp> using boost::asio::ip::tcp; class TcpClient { public: TcpClient(boost::asio::io_context& io_context) : io_context_(io_context), socket_(io_context) {} void Connect(const std::string& host, const std::string& port) { tcp::resolver resolver(io_context_); tcp::resolver::results_type endpoints = resolver.resolve(host, port); boost::asio::async_connect(socket_, endpoints, [this](boost::system::error_code ec, tcp::endpoint endpoint) { if (!ec) { std::cout << "Connected to server: " << endpoint << std::endl; Read(); } else { std::cout << "Failed to connect: " << ec.message() << std::endl; } }); } void Read() { boost::asio::async_read(socket_, boost::asio::buffer(data_, max_length), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { std::cout << "Received data: " << std::string(data_, length) << std::endl; Read(); } else { std::cout << "Failed to read data: " << ec.message() << std::endl; } }); } void Write(const std::string& message) { boost::asio::async_write(socket_, boost::asio::buffer(message), [this](boost::system::error_code ec, std::size_t /*length*/) { if (ec) { std::cout << "Failed to write data: " << ec.message() << std::endl; } }); } private: boost::asio::io_context& io_context_; tcp::socket socket_; enum { max_length = 1024 }; char data_[max_length]; }; int main() { boost::asio::io_context io_context; TcpClient client(io_context); client.Connect("localhost", "1234"); // 发送数据示例 client.Write("Hello, server!"); io_context.run(); return 0; } ``` 在上述示例代码中,`TcpClient` 类负责连接服务器、读取和写入数据。`Connect` 方法通过异步连接操作连接到指定的主机和端口,`Read` 方法通过异步读取操作接收服务器发送的数据,`Write` 方法通过异步写入操作向服务器发送数据。 你可以根据自己的需求修改以上示例代码,以适应你的具体应用场景。希望对你有帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值