Delphi跨平台Socket安卓客户端连接离线服务器卡顿问题咨询
问题根因
- 你使用的
System.Net.Socket.TSocket的Connect方法默认是阻塞同步调用,当服务端离线时,调用会卡在操作系统内核的TCP连接超时逻辑中,这个过程不受应用层的线程终止标记、对象释放操作控制,强制释放还在阻塞调用中的Socket对象会直接触发内存访问错误。 - 你现有的线程嵌套逻辑(FThread里又开RThread)、Sleep硬等500ms的逻辑也有逻辑漏洞,会导致状态判断混乱。
修复方案
1. 改用带超时的非阻塞连接逻辑
使用Connect的超时重载,发起连接后最多等待指定时长,超时就主动放弃,不会一直卡住。
2. 简化线程模型,单线程处理连接+重试+收消息逻辑
不需要开两层匿名线程,单个工作线程循环处理:连接失败就间隔几秒重试,连接成功就处理收消息逻辑,断开后自动回到重连流程,线程终止信号触发时直接退出循环即可。
3. 线程安全的状态管理,避免跨线程直接操作Socket对象
用事件信号控制线程退出,不要在主线程销毁阶段直接释放还在工作线程中使用的Socket对象。
修复后参考代码
uses System.SysUtils, System.Net.Socket, System.SyncObjs; var Form1: TForm1; FClientSocket: TSocket; FWorkThread: TThread; FConnected: Boolean; FTerminateFlag: TEvent; // 用事件信号控制线程退出,响应更及时 procedure TForm1.FormCreate(Sender: TObject); begin FConnected := False; FTerminateFlag := TEvent.Create(nil, False, False, ''); // 单工作线程处理所有连接、重连、收消息逻辑 FWorkThread := TThread.CreateAnonymousThread(procedure var WaitResult: TWaitResult; ConnectSuccess: Boolean; begin while not FWorkThread.Terminated do begin // 等待退出信号,间隔3秒发起一次重连 WaitResult := FTerminateFlag.WaitFor(3000); if WaitResult = wrSignaled then Break; if FConnected then Continue; // 已连接则跳过重连逻辑 try FClientSocket := TSocket.Create(TSocketType.TCP, TProtocolType.IP); try // 核心:带2秒超时的连接逻辑,超时未成功直接放弃 ConnectSuccess := FClientSocket.Connect('192.168.1.155', 4851, 2000); if ConnectSuccess then begin FConnected := True; // 连接成功后进入收消息循环 while FConnected and not FWorkThread.Terminated do begin // 收消息同样加超时,避免一直阻塞 if FClientSocket.CanRead(1000) then begin // 此处添加你的消息处理逻辑 // 如果接收失败/抛出异常,将FConnected设为False即可触发重连 end; end; end; finally // 退出当前逻辑时安全释放Socket FConnected := False; FreeAndNil(FClientSocket); end; except // 捕获连接、接收阶段的所有异常,避免线程崩溃 FConnected := False; FreeAndNil(FClientSocket); end; end; end); FWorkThread.FreeOnTerminate := False; FWorkThread.Start; end; procedure TForm1.FormDestroy(Sender: TObject); begin // 先触发退出信号,唤醒等待中的工作线程 if Assigned(FTerminateFlag) then FTerminateFlag.SetEvent; // 安全终止工作线程 if Assigned(FWorkThread) then begin FWorkThread.Terminate; FWorkThread.WaitFor; FreeAndNil(FWorkThread); end; // 释放事件对象 FreeAndNil(FTerminateFlag); end;
内容的提问来源于stack exchange,提问作者Jacck Cooper
相关产品推荐
相关产品推荐

