升级到C++Builder 11后TIdTCPServer的Execute事件不触发如何解决
问题现象
原有代码在CBuilder 10.3 Rio版本中可正常实现跨网络文件流传输,升级到CBuilder 11 Alexandria版本后出现以下异常:
fileStreamerExecute()回调函数从未被触发- 指定路径下可生成目标文件,但文件长度为0且持续处于占用状态,所有打开该文件的操作都会失败
//--------------------------------------------------------------------------- void Tdata::stream_file(String path, Tstream_dir direction) { _stream_path = path; _stream_dir = direction; if (_fs) delete _fs; if (_stream_dir == sdIn) _fs = new TFileStream(_stream_path, fmCreate | fmShareDenyNone); else _fs = new TFileStream(_stream_path, fmOpenRead | fmShareDenyNone); fileStreamer->DefaultPort = 9965; fileStreamer->Active = true; } //--------------------------------------------------------------------------- void __fastcall Tdata::fileStreamerExecute(TIdContext *AContext) { if (_stream_dir == sdIn) { try { _fs->Position = 0; AContext->Connection->IOHandler->ReadStream(_fs, -1, true); } __finally { delete _fs; _fs = 0; } AContext->Connection->Disconnect(); } else { try { AContext->Connection->IOHandler->Write(_fs); } __finally { delete _fs; _fs = 0; } AContext->Connection->Disconnect(); } } // --------------------------------------------------------------------------- __fastcall TstartForm::TstartForm(TComponent* Owner) : TForm(Owner) { char hostname[256]; String host_ip = ""; _progressForm = 0; if (gethostname(hostname, 256) == 0) { hostent *host = gethostbyname(hostname); if (host) { char *addr = host->h_addr; if (addr) { host_ip = String(inet_ntoa(*(in_addr*) addr)); // returns "192.168.180.1" from VMWare network adaptor // hard code to correct value for testing host_ip = "192.168.1.226"; } } } }
根因分析
问题出在本地IP获取逻辑:
旧代码使用WinSock原生API gethostbyname 获取本地IP,该接口在多网卡环境(存在VMware虚拟网卡、VPN网卡等)下,返回的第一个IP并非实际用于业务通信的物理网卡IP。C++Builder 11集成的Indy组件对套接字绑定、连接校验的逻辑做了更新,若监听/连接使用的IP与实际业务网卡不匹配,客户端连接无法被正确分发到OnExecute事件回调。而已经创建的TFileStream因为回调未触发,__finally块中的资源释放逻辑永远不会执行,导致文件一直被占用、没有写入内容。
修复方案
改用Indy内置接口获取本地IP即可解决问题:
初步修复版本
__fastcall TstartForm::TstartForm(TComponent* Owner) : TForm(Owner) { _progressForm = 0; String host_ip = L""; TIdStackWindows* id_stack = new TIdStackWindows; if (id_stack) { host_ip = id_stack->LocalAddress; delete id_stack; } }
官方推荐优化版本
根据Indy维护者Remy的建议,不建议手动实例化TIdStack子类,应使用全局GStack实例的标准操作方式:
__fastcall TstartForm::TstartForm(TComponent* Owner) : TForm(Owner) { _progressForm = 0; String host_ip = L""; TIdStack::IncUsage(); host_ip = GStack->LocalAddress; TIdStack::DecUsage(); sm->_this_computer_id = host_ip; }
内容的提问来源于stack exchange,提问作者user1455270
相关产品推荐
相关产品推荐

