Boost.Asio实现TCP客户端连接无监听的localhost端口不报错如何解决
问题原因分析
- 核心bug出在connect函数的前置判断逻辑和连接失败后的资源处理流程:
- 当第一次连接错误IP失败时,你已经执行了
signal_socket.reset(new boost::asio::ip::tcp::socket(my_context)),signal_socket指针已经被赋值为非空。同步connect调用如果失败,已经创建的socket对象仍然会处于is_open()返回true的状态,因为connect前会自动打开socket,连接失败不会自动关闭这个socket。 - 第二次调用connect连接本地IP时,会命中开头的
if (signal_socket != NULL && signal_socket->is_open() ) return true判断,直接返回成功,根本没有执行实际的连接逻辑,所以不管8887端口有没有监听,都会返回成功。
- 当第一次连接错误IP失败时,你已经执行了
- 另外你传入了error_code参数调用connect,Boost.Asio的这类重载不会抛出异常,外层的try-catch完全没有作用。
修复方案
- 先修改连接失败后的清理逻辑,连接出错时重置socket指针、回滚状态,参考修改后的代码:
bool SimpleClient::connect(const char* ip_address) { boost::system::error_code ec; // 前置判断增加状态校验,只有确实处于已连接状态才直接返回成功 if (signal_socket != NULL && signal_socket->is_open() && state == ClientState::CL_READY) return true; // 每次重连前先清理旧的socket资源 if (signal_socket != NULL) { boost::system::error_code ignore_ec; signal_socket->close(ignore_ec); signal_socket.reset(); } state = ClientState::CL_UNCON; boost::asio::ip::tcp::endpoint signal_endpoint(boost::asio::ip::make_address(ip_address), 8887); signal_socket.reset(new boost::asio::ip::tcp::socket(my_context)); signal_socket->connect(signal_endpoint, ec); if(ec) { std::cout<<ec.message()<<" "<<ec.value()<<std::endl; // 连接失败,清理socket boost::system::error_code ignore_ec; signal_socket->close(ignore_ec); signal_socket.reset(); return false; } // 增加额外的连接有效性校验 if (!signal_socket->is_open() || signal_socket->remote_endpoint(ec).port() != 8887) { boost::system::error_code ignore_ec; signal_socket->close(ignore_ec); signal_socket.reset(); return false; } std::cout<<signal_socket<<std::endl; state = ClientState::CL_READY; return true; }
- 可选的额外连接存活检测:如果要避免后续连接因为网络问题静默断开,可以给socket设置TCP_KEEPALIVE选项,或者在每次收发数据前都校验socket状态和操作返回的错误码。
内容的提问来源于stack exchange,提问作者Gionata Benelli
相关产品推荐
相关产品推荐

