Delphi代码实现了多种解决"掉线"问题的策略,包括启用TCP保活定时器、自动重连机制、错误处理、以及心跳包等方法。以下是代码的格式化和详细注释说明:
// 1. TCP 保活定时器设置
type
TCP_KeepAlive = record
OnOff: Cardinal; // 开启保活标志
KeepAliveTime: Cardinal; // 保活时间(毫秒)
KeepAliveInterval: Cardinal; // 保活间隔(毫秒)
end;
procedure TFrm_Client.Sck_MainConnect(Sender: TObject; Socket: TCustomWinSocket);
var
val: TCP_KeepAlive;
Ret: DWord;
begin
inherited;
try
// 设置TCP保活定时器
val.OnOff := 1; // 开启保活
val.KeepAliveTime := 1000 * 60 * 60 * 24; // 24小时
val.KeepAliveInterval := 100; // 每100毫秒发送一次保活包
Ret := WSAIoctl(Socket.SocketHandle, IOC_IN or IOC_VENDOR or 4,
@val, SizeOf(val), nil, 0, @Ret, nil, nil);
finally
ShowInfo('已连接到服务器. TCP_KeepAlive定时器设置时间: ' + IntToStr(val.KeepAliveTime) + 'ms, 结果: ' + IntToStr(Ret));
end;
end;
// 2. 处理错误码并触发重连机制
procedure TFrm_Client.Sck_MainError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
var
nCode: Cardinal;
begin
inherited;
ShowInfo('||[Error] 出错: ' + IntToStr(ErrorCode) + '; 自动重连: ' + BoolToStr(lAskForReConnect, true));
nCode := ErrorCode;
ErrorCode := 0;
// 错误码判断,若发生断线错误则尝试重连
if (nCode = EConnAborted) or (nCode = ENetDown) or (nCode = ETimedOut) or
(nCode = EConnRefused) or (nCode = EHostUnReach) then
begin
if lAskForReConnect and (not lInReConnecting) then
Timer_ReConnect.Enabled := True; // 启动自动重连
if not lInReConnecting then
begin
sck_Main.Active := False;
sck_Main.Close; // 关闭连接
end;
ShowInfo('连接状态: ' + BoolToStr(sck_Main.Active, true) + '; 重连设置: ' +
BoolToStr(lAskForReConnect, true) + '; 重连状态: ' + BoolToStr(Timer_ReConnect.Enabled, true));
end;
end;
// 3. 断开连接时自动重连
procedure TFrm_Client.Sck_MainDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
inherited;
if (not lAskForReConnect) or (lInReConnecting) then Exit;
Timer_ReConnect.Enabled := True; // 启动重连定时器
end;
// 4. 实现重连机制
function TFrm_Client.ReConnectServer: Boolean;
const
nMax = 6; // 最大重试次数
var
i: Integer;
begin
Result := False;
i := 1;
if not lAskForReConnect then Exit;
with sck_Main do
begin
if not lInCreatting then
begin
try
// 断开连接并尝试重新连接
sck_Main.Socket.Disconnect(sck_Main.Socket.Handle);
sck_Main.Close;
while (not sck_Main.Active) and (i <= nMax) do
begin
ShowInfo('* 尝试自动重新连接 [' + IntToStr(i) + '/' + IntToStr(nMax) + ']...');
sck_Main.Open;
Delay(2000);
if not sck_Main.Active then
Delay(1000);
Inc(i);
end;
except
on E: Exception do;
end;
end;
end;
lLoginOK := sck_Main.Active;
if not lLoginOK then
begin
ShowInfo('已经断开服务器的连接, 正在尝试重新连接');
lbl_Move.Caption := '已经断开连接, 正在尝试重新连接';
end;
Result := lLoginOK;
end;
// 5. 自动重连定时器
procedure TFrm_Client.Timer_ReconnectTimer(Sender: TObject);
begin
inherited;
try
if (sck_Main.Active) or (not lAskForReConnect) then Exit;
lInReConnecting := True; // 正在连接中
Timer_ReConnect.Enabled := False; // 禁用重连定时器
Timer_ReConnect.Enabled := (not ReConnectServer) and lAskForReConnect;
finally
lInReConnecting := False;
end;
end;
// 6. 发送心跳包保持连接
procedure TFrm_Client.Timer_KeepConnectTimer(Sender: TObject);
var
msg: _msgIdle;
begin
inherited;
if (not lLoginOK) or (lInReConnecting) then Exit;
try
if lGlobalTerminate then Exit;
Timer_KeepConnect.Enabled := False;
msg.Version.dwMajorVersion := frm_MDI.Version.dwMajorVersion;
msg.Version.dwMinorVersion := frm_MDI.Version.dwMinorVersion;
msg.nTickCount := 0; // 设置心跳包的时间戳
msg.nHandle := 0; // 设置句柄
msg.sComment := '你好!有空常联系!'; // 心跳包的内容
if not sendMessage(sck_Main.Socket, msgTypeIdle, @msg) then
ShowInfo('心跳包发送失败!');
finally
Timer_KeepConnect.Enabled := True;
ShowInfo('发送联系服务器消息IDLE, 结果: ' + BoolToStr(True, true));
end;
end;
主要策略:
- TCP保活定时器:通过设置
KeepAliveTime
和KeepAliveInterval
,确保连接在无数据传输时仍能保持活跃。 - 错误处理:通过捕获常见的连接错误(如
10053
)进行断开处理,并触发自动重连机制。 - 自动重连:在连接中断后,通过
Timer_ReConnect
定时器来尝试重新连接,最多重试nMax
次。 - 心跳包:定时发送消息给服务器,防止连接因超时而断开。
通过结合这些方法,你可以有效地提高网络连接的稳定性。