如何断开非活动客户端与 TIdTCPServer 的连接?
how do I disconnect inactive clients with TIdTCPServer?
我正在尝试断开连接到 TIdTCPServer
的非活动客户端,无论这些客户端是从其 Internet 断开连接还是在一段时间内处于非活动状态。
我尝试在 OnConnect
事件中设置超时,如下所示:
procedure TservForm.TcpServerConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.ReadTimeout := 26000;
AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000);
end;
但是客户端连接丢失后好像并没有触发断开连接。
我尝试使用 SetKeepAliveValues()
,但断开非活动客户端的连接需要花费太多时间。
是否有更有用的方法来断开非活动客户端的连接?那么如果客户端没有收到或发送任何东西,例如30秒后,服务器会断开连接吗?
执行事件时
procedure TservForm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
cmd: String;
Cache, OutboundCmds: TStringList;
MS: TMemoryStream;
I: integer;
S: String;
begin
Connection := AContext as TConnection;
// check for pending outbound commands...
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.Writeln(OutboundCmds.Strings[I],
IndyTextEncoding_UTF8);
MS := TMemoryStream(OutboundCmds.Objects[I]);
if MS <> nil then
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := true;
AContext.Connection.IOHandler.Write(MS, 0, true);
end;
end;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Exit;
end;
end;
cmd := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8);
...............
...............
客户端不会断开连接,因为在空闲时间没有达到ReadLn()
,所以ReadTimeout
没有效果,如果你没有发送很多命令,那么套接字缓冲区没有填满所以 SO_SNDTIMEO
也没有效果。
由于您已经在进行一些手动超时处理,因此您可以对其进行扩展以处理空闲超时,例如:
type
TConnection = class(TIdServerContext)
...
public
LastSendRecv: LongWord;
...
end;
...
procedure TservForm.TcpServerConnect(AContext: TIdContext);
var
Connection: TConnection;
begin
Connection := AContext as TConnection;
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadTimeout := 30000;
AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000);
Connection.LastSendRecv := Ticks;
end;
procedure TservForm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
cmd: String;
Cache, OutboundCmds: TStringList;
MS: TMemoryStream;
I: integer;
S: String;
begin
Connection := AContext as TConnection;
// check for pending outbound commands...
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]);
MS := TMemoryStream(OutboundCmds.Objects[I]);
if MS <> nil then
AContext.Connection.IOHandler.Write(MS, 0, true);
end;
Connection.LastSendRecv := Ticks;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
if GetTickDiff(Connection.LastSendRecv, Ticks) >= 30000 then
AContext.Connection.Disconnect;
Exit;
end;
end;
cmd := AContext.Connection.Socket.ReadLn;
Connection.LastSendRecv := Ticks;
...
end;
我正在尝试断开连接到 TIdTCPServer
的非活动客户端,无论这些客户端是从其 Internet 断开连接还是在一段时间内处于非活动状态。
我尝试在 OnConnect
事件中设置超时,如下所示:
procedure TservForm.TcpServerConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.ReadTimeout := 26000;
AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000);
end;
但是客户端连接丢失后好像并没有触发断开连接。
我尝试使用 SetKeepAliveValues()
,但断开非活动客户端的连接需要花费太多时间。
是否有更有用的方法来断开非活动客户端的连接?那么如果客户端没有收到或发送任何东西,例如30秒后,服务器会断开连接吗?
执行事件时
procedure TservForm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
cmd: String;
Cache, OutboundCmds: TStringList;
MS: TMemoryStream;
I: integer;
S: String;
begin
Connection := AContext as TConnection;
// check for pending outbound commands...
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.Writeln(OutboundCmds.Strings[I],
IndyTextEncoding_UTF8);
MS := TMemoryStream(OutboundCmds.Objects[I]);
if MS <> nil then
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := true;
AContext.Connection.IOHandler.Write(MS, 0, true);
end;
end;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Exit;
end;
end;
cmd := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8);
...............
...............
客户端不会断开连接,因为在空闲时间没有达到ReadLn()
,所以ReadTimeout
没有效果,如果你没有发送很多命令,那么套接字缓冲区没有填满所以 SO_SNDTIMEO
也没有效果。
由于您已经在进行一些手动超时处理,因此您可以对其进行扩展以处理空闲超时,例如:
type
TConnection = class(TIdServerContext)
...
public
LastSendRecv: LongWord;
...
end;
...
procedure TservForm.TcpServerConnect(AContext: TIdContext);
var
Connection: TConnection;
begin
Connection := AContext as TConnection;
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadTimeout := 30000;
AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000);
Connection.LastSendRecv := Ticks;
end;
procedure TservForm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
cmd: String;
Cache, OutboundCmds: TStringList;
MS: TMemoryStream;
I: integer;
S: String;
begin
Connection := AContext as TConnection;
// check for pending outbound commands...
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]);
MS := TMemoryStream(OutboundCmds.Objects[I]);
if MS <> nil then
AContext.Connection.IOHandler.Write(MS, 0, true);
end;
Connection.LastSendRecv := Ticks;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
if GetTickDiff(Connection.LastSendRecv, Ticks) >= 30000 then
AContext.Connection.Disconnect;
Exit;
end;
end;
cmd := AContext.Connection.Socket.ReadLn;
Connection.LastSendRecv := Ticks;
...
end;