处理程序已由A类注册.例如:
boost::asio::async_read(s,b,boost::bind(&A::F,this->shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
在调试器下,我可以访问适当的io_service变量.如何找出(A :: F,this,s,b)尚未完成的操作.
解决方法
示例程序
要通过示例显示调试,请从侦听端口4321的基本UDP echo服务器开始:
#include <boost/array.hpp> #include <boost/asio.hpp> #include <boost/bind.hpp> using boost::asio::ip::udp; class udp_echo { public: udp_echo(boost::asio::io_service& service,unsigned int port) : socket_(service,udp::endpoint(udp::v4(),port)) { socket_.async_receive_from( boost::asio::buffer(buffer_),sender_,boost::bind(&udp_echo::handle_receive,boost::asio::placeholders::bytes_transferred)); } void handle_receive(const boost::system::error_code& error,std::size_t bytes_transferred) { socket_.async_send_to( boost::asio::buffer(buffer_,bytes_transferred),boost::bind(&udp_echo::handle_send,boost::asio::placeholders::bytes_transferred)); } void handle_send(const boost::system::error_code& error,std::size_t bytes_transferred) { socket_.close(); } private: udp::socket socket_; boost::array<char,128> buffer_; udp::endpoint sender_; }; int main() { boost::asio::io_service service; udp_echo echo(service,4321); service.run(); }
这个简单的程序有一个异步调用链:
udp_echo::udp_echo() { socket_.async_receive_from(...); --. } | .-----------------------' v void udp_echo::handle_receive(...) { socket_.async_send_to(...); ------. } | .-----------------------' v void udp_echo::handle_send() { socket_.close(); }
处理程序跟踪
Boost 1.47引入了handler tracking.只需定义BOOST_ASIO_ENABLE_HANDLER_TRACKING和Boost.Asio就会将调试输出(包括时间戳)写入标准错误流.运行编程,并通过UDP发送“hello world”产生以下输出:
@asio|1363273821.846895|0*1|socket@0xbf8c4e3c.async_receive_from // 1 @asio|1363273829.288883|>1|ec=system:0,bytes_transferred=12 // 2 @asio|1363273829.288931|1*2|socket@0xbf8c4e3c.async_send_to // 3 @asio|1363273829.289013|<1| // 4 @asio|1363273829.289026|>2|ec=system:0,bytes_transferred=12 // 5 @asio|1363273829.289035|2|socket@0xbf8c4e3c.close // 6 @asio|1363273829.289075|<2| // 7
它可以逐行读取:
>非处理程序(0)调用socket.async_receive_from(),创建处理程序1.
>输入处理程序1 socket.async_receive_from(),没有错误,已收到12个字节.
> Handler 1 socket.async_receive_from()调用socket.async_send_to(),创建处理程序2.
>退出处理程序1 socket.async_receive_from().
>输入处理程序2 socket.async_send_to(),已发送12个字节.
> Handler 2调用了socket.close().
>退出处理程序2 socket.async_send_to().
并直观地映射到以下内容:
udp_echo::udp_echo() { socket_.async_receive_from(...); --. // 1 } | .-----------------------' v void udp_echo::handle_receive(...) { // 2 socket_.async_send_to(...); ------. // 3 } | // 4 .-----------------------' v void udp_echo::handle_send() { // 5 socket_.close(); // 6 } // 7
GDB
通过GDB进行调试需要挖掘多个层.它有助于了解Boost.Asio的一些实现细节.以下是一些概念:
> io_service仅包含准备运行的处理程序.
>反应器通常包含工作操作,以及未准备好运行的完成处理程序的句柄.
> reactor将使用io_service注册自己.
这是一个调试会话:
(gdb) bt #0 0x00ab1402 in __kernel_vsyscall () #1 0x00237ab8 in __epoll_wait_nocancel () from /lib/libc.so.6 #2 0x080519c3 in boost::asio::detail::epoll_reactor::run (this=0x80560b0,block=true,ops=...) at /opt/boost/include/boost/asio/detail/impl/epoll_reactor.ipp:392 #3 0x08051c2d in boost::asio::detail::task_io_service::do_run_one ( this=0x8056030,lock=...,this_thread=...,ec=...) at /opt/boost/include/boost/asio/detail/impl/task_io_service.ipp:396 #4 0x08051e8a in boost::asio::detail::task_io_service::run (this=0x8056030,ec=...) at /opt/boost/include/boost/asio/detail/impl/task_io_service.ipp:153 #5 0x08051f50 in boost::asio::io_service::run (this=0xbfffe818) at /opt/boost/include/boost/asio/impl/io_service.ipp:59 #6 0x08049a44 in main () at example.cpp:48 (gdb) frame 6 #6 0x08049a44 in main () at example.cpp:48 48 service.run();
首先,需要找到反应堆服务.需要进行向下转换,因此我们使用调试器来定位某些类型:
(gdb) p service.service_registry_.init_keytab init_key init_key<boost::asio::datagram_socket_service<boost::asio::ip::udp> > init_key<boost::asio::detail::epoll_reactor> init_key<boost::asio::detail::task_io_service>
每个密钥都与特定服务相关联,所有服务都在service.service_registry_中的链表中维护.类型信息与它们相关联,允许我们识别所需的服务.
(gdb) set $service = service.service_registry_.first_service_ (gdb) p $service.key_.type_info_.__name $1 = 0x8052b60 "N5boost4asio6detail14typeid_wrapperINS0_23datagram_socket_serviceINS0_2ip3udpEEEEE"
那就是boost :: asio :: datagram_socket_service< boost :: asio :: ip :: udp>,所以继续下一个:
(gdb) set $service = $service.next_ (gdb) p $service.key_.type_info_.__name $2 = 0x8052cc0 "N5boost4asio6detail14typeid_wrapperINS1_13epoll_reactorEEE"
$service现在指向reactor服务器.根据init_key类型参数downcast服务:
(gdb) set $service = *('boost::asio::detail::epoll_reactor'*) $service
具有工作的优秀处理程序位于反应堆内的操作链表中:
(gdb) set $ops = $service.registered_descriptors_.live_list_.op_queue_ (gdb) set $op = $ops.front_ (gdb) p *$op $3 = {<boost::asio::detail::task_io_service_operation> = {next_ = 0x0,func_ = 0x804c256 <boost::asio::detail::reactive_socket_recvfrom_op< boost::asio::mutable_buffers_1,boost::asio::ip::basic_endpoint< boost::asio::ip::udp>,boost::_bi::bind_t<void,boost::_mfi::mf2<void,udp_echo,boost::system::error_code const&,unsigned int>,boost::_bi::list3<boost::_bi::value<udp_echo*>,boost::arg<1> (*)(),boost::arg<2> (*)()> > >:: do_complete(boost::asio::io_service::io_service_impl*,boost::asio::detail::epoll_reactor::descriptor_state::operation*,size_t)>,task_result_ = 0},ec_ = { m_val = 11,m_cat = 0x13b2c8},bytes_transferred_ = 0,perform_func_ = 0x80514c8 <boost::asio::detail::reactive_socket_recvfrom_op_base< boost::asio::mutable_buffers_1,boost::asio::ip::basic_endpoint<boost::asio::ip::udp> >::do_perform(boost::asio::detail::reactor_op*)>}
另一个垂头丧气是必需的.将$op转换为func_成员函数指针所属的类.
(gdb) set $op = *('boost::asio::detail::reactive_socket_recvfrom_op< boost::asio::mutable_buffers_1,boost::asio::ip::basic_endpoint< boost::asio::ip::udp>,boost::_mfi::mf2< void,boost::arg<2> (*)()> > >'*) $op
此操作包含所需信息.
缓冲区:
(gdb) p $op.buffers_ $4 = {<boost::asio::mutable_buffer> = {data_ = 0xbfffe77c,size_ = 128},<No data fields>} (gdb) p &echo.buffer_ $5 = (boost::array<char,128u> *) 0xbfffe77c
这个例子:
(gdb) p $op.handler_.l_.a1_.t_ $6 = (udp_echo *) 0xbfffe768 (gdb) p &echo $7 = (udp_echo *) 0xbfffe768
成员函数指针:
(gdb) p $op.handler_.f_.f_ $8 = (void (udp_echo::*)(udp_echo *,const boost::system::error_code &,unsigned int)) 0x80505b0 < udp_echo::handle_receive(boost::system::error_code const&,size_t)>
套接字信息:
(gdb) p $op.socket_ $9 = 10 (gdb) p echo.socket_.implementation.socket_ $10 = 10
在这种情况下,操作只知道本机套接字表示(文件描述符).确定它是什么套接字的一种有用方法是查询lsof.
$/usr/sbin/lsof -i -P | grep a.out a.out 4265 ghost 10u IPv4 1166143 UDP *:4321
因此,文件描述符10正在UDP 4321上侦听.