Reputation: 61
I use Indy10 and Delphi XE in a Windows service application with a TIdCmdTCPServer serving clients using a TIdTCPClient.
With this function
function ConnectionCount: Integer;
begin
if IdCmdTCPServer.Contexts = nil then
Exit(0);
with IdCmdTCPServer.Contexts do
try
Result := LockList.Count
finally
UnlockList
end;
end;
I get a count of connections from clients to the server. Sometimes this count doesn't go down to zero even when all clients are closed. I guess those connections are dead somehow. On a site with 5 clients I've seen a connection count of 35 after some weeks of operation.
Is there a way to detect that a connection is dead and release it?
I suspect a connection can become dead if the client freezes or gets killed, but I don't know if that's the case here.
EDIT: here is the code from one of my TIdCmdTCPServer handlers:
procedure TDSA_Service.ModeHandler(ASender: TIdCommand);
var
R: TToolReply;
begin
FBMutex.Enter;
try
try
R := SystemServer.Mode(GetIntParam(0, 'SystemNo', ASender));
try
Reply(ASender, R)
finally
R.Free
end
except
HandleException;
Abort
end
finally
FBMutex.Leave
end
end;
HandleException is madExcept sending a bug report. Am I right that some indy exceptions shouldn't be caught here? Which exceptions?
Upvotes: 0
Views: 341
Reputation: 2287
You can't really determine if a connection is "dead". What I do is decide if the connection has been around for longer than say, 24 hours, and delete it if it has. I haven't cared about the issue that Remy raised "why connections hang around". My users also don't mind if they come in the next morning only to find that their client is no longer responsive. I hope this code answers more questions than it raises.
type
TClientData = class(TObject)
ClientName: string;
ClientHost: string;
ID: TDateTime;
end;
type
TDateTimeO = Class(TObject)
public
Value: TDateTime;
constructor Create(NewValue: TDateTime);
end;
const
MAXCONNECT = (24 * 60 * 60);
procedure TfrmMain.IdCmdTCPServer1Connect(AContext: TIdContext);
var
myClient: TClientData;
begin
AContext.Connection.IOHandler.DefStringEncoding := IdGlobal.IndyTextEncoding_8Bit;
myClient := TClientData(AContext.Data);
if myClient = nil then
begin
myClient := TClientData.Create;
AContext.Data := myClient;
myClient.ClientName := '<Unknown>';
myClient.ClientHost := AContext.Binding.PeerIP;
myClient.ID := Now;
end;
// this actually happens elsewhere in my server code, but I've pasted
// it here for convenience
ListBox1.Items.AddObject(
myClient.ClientName + '=' +
FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
TDateTimeO.Create(myClient.ID));
end;
procedure TfrmMain.Timer1Timer(Sender: TObject);
var
ThisMoment: TDateTime;
i, j: Integer;
List: TList;
Name: String;
TimeStamp: TDateTime;
MyClient: TClientData;
begin
ThisMoment := Now;
List := IdCmdTCPServer1.Contexts.LockList;
try
for i := ListBox1.Count - 1 downto 0 do
begin
// now terminate the actual connection (if it can be found)
Name := Trim(TokenStr('=', ListBox1.Items[i], 1));
TimeStamp := TDateTimeO(ListBox1.Items.Objects[i]).Value;
if (SecondsBetween(TimeStamp, ThisMoment) > MAXCONNECT)
then
begin
for j := List.Count - 1 downto 0 do
begin
if Assigned(List[j]) then
begin
// is this the corresponding connection?
myClient := TClientData(TIdContext(List[j]).Data);
if (Name = myClient.ClientName) and
(TDateTimeO(ListBox1.Items.Objects[i]).Value = myClient.ID) then
begin
TIdContext(List[j]).Connection.Disconnect;
BREAK;
end;
end;
end; // next j
// sometimes the connection isn't in the locklist for some reason
// delete the listbox entry anyway
TDateTimeO(ListBox1.Items.Objects[i]).Free;
ListBox1.Items.Delete(i); // and delete the list entry
end;
end; // next i
finally
IdCmdTCPServer1.Contexts.UnLockList;
end;
end;
Upvotes: 2