背景:
本系统为我公司开发的一套商用车车联网系统的网关子系统,采用C++基于BOOST库开发,网络开发库采用了BOOST的ASIO库,线程池采用了基于BOOST的ThreadPool库。
本次问题中涉及两个线程在不同时刻对同一个SOCKET的操作,这两个线程分属不同的类。
现象:
系统接入实车进行测试后,在某些场景开会在read时发生异常,或直接抛出错误,或连续多次读到长度为0的数据包(甚至进入死循环),这时候,程序会关闭SOCKET连接,但是若在此时,另一线程正好正在对此SOCKET进行写操作,则会导致系统崩溃
定位:
core dump 文件:
#1 0x0000000000451b64 in MessageSender::send(HDConstants::TaskDef*, int) ()
#2 0x0000000000473a0a in ThreadPoolGateway::abort(HDConstants::TaskDef*, SQ::Memory**, bool, int) ()
#3 0x0000000000470cc3 in ThreadPoolGateway::task(HDConstants::TaskDef*) ()
#4 0x0000000000442e2e in boost::function0<void>::operator()() const ()
#5 0x0000000000444aa3 in
boost::threadpool::detail::worker_thread<boost::threadpool::detail::pool_core<boost::function0<void>, boost::threadpool::fifo_scheduler, boost::threadpool::static_size, boost::threadpool::resize_controller, boost::threadpool::wait_for_all_tasks> >::run() ()
#6 0x00007f41b0ded1d0 in thread_proxy () from ./lib/libboost_thread.so.1.60.0
#7 0x000000322a2079d1 in start_thread () from /lib64/libpthread.so.0
#8 0x0000003229ee8b6d in clone () from /lib64/libc.so.6
日志文件:
2016-09-05 15:12:13.432408 [0x00004410-0x00007f4584405700] [1]: LocalTCPConnection::handleRead received msg length:0 ipaddr = 117.136.98.68 port = 10189
2016-09-05 15:12:13.432499 [0x00004410-0x00007f45906d5700] [0]: MessageSender::send enter
2016-09-05 15:12:13.432505 [0x00004410-0x00007f4584405700] [1]: ipaddr= 117.136.98.68 port= 10189 tcp connection closed. error: End of file
2016-09-05 15:12:13.432525 [0x00004410-0x00007f45906d5700] [1]: MessageSender::send response msg:7e8001000501150204111601ff01ff000200937e
从COREDUMP文件中看,系统崩溃前正在执行send操作,结合日志,发现此时另一线程发现读到了长度为0的数据包,进而执行了断开TCP的操作,而此时SEND操作还未被执行完,从而导致系统崩溃。
解决方案:
查询代码,其断开TCP的处理代码如下:
if(e || bytesTransferred == 0){
status = CONNECTIONCLOSED;
boost::system::error_code ec;
socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
try
{
LOG_DEBUG<<"ipaddr= "<<addr4<<" port= "<<port<<" tcp connection closed. error: "<<e.message()<<endl;
socket.close();
}
catch(boost::system::error_code& ex)
{
LOG_DEBUG<<"ipaddr= "<<addr4<<" port= "<<port<<"catch exception ex:"<<ex.message()<<endl;
}
return;
}
此处理比较简单直接,直接关闭了TCP的读写操作,然后释放了TCP连接的资源。