I am having problem with IdTCPclient.connected function from Indy in Delphi. I am using Indy10 and Delphi2010 environment. My problem is every time i check the TCP connection with IdTCPclient.connected, it raises exception with these errors EidSocketError, EidReadTimeOut. Is there any way to disconnect and reconnect the connection? (like reset the connection).
Note: I set TCPClient.ReTimeout:= 30000;
The implemented coding for reset the connection is follow.
if IdTCPclient.connected then
begin
IdTCPclient.IOHandler.InputBuffer.Clear;
IdTCPclient.Disconnect;
end;
sleep(1000);
try
IdTCPclient.connect;
except
on E: Exception do
MessageDlg('Connecting Error: '+E.Message, mtError, [mbOk], 0);
end;
But some point, i get exception and it couldn't connect at all. I am not sure what i am doing wrong.
Should i do this?
Disconnect first
Clear input buffer
Destroy TCPclient
Re-create new TCPclient
And then connect it again
If it is the case, can someone provide me a way how to do it properly?
Also, there is another function to re-connecting the TCP in my coding. It also gives me exception as well. I check the connecting before i send a message to TCP. If there is no connection, i reconnect for five times.
result := IdTCPclient.connected
if not result then
begin
for k:=0 to 4 do
beign
sleep(1000);
try
TCPclient.connect;
except
on E: Exception do
MessageDlg('Connecting Error: '+E.Message, mtError, [mbOk], 0);
end
result := TCPclient.connected;
if result then break;
end;
With above two coding, program handles reconnecting and reset the connection pretty well. But some point the program cannot re-connect or reset the connection at all.
What should i do when i get exception? Should i reconnect from exception?
How do we build coding to check the connection regularly?
How do we build coding to to get back the connection when it lost?
Kind regards,
Connected() should not be raising any exceptions at all. If it is, then it is likely a bug. Please provide a stack trace showing that.
The best option is to simply avoid using Connected() whenever possible. When you need to perform an I/O operation, just do so, and let Indy raise an exception if a failure occurs. You can then handle it at that moment, eg:
try
IdTCPClient.DoSomething...
except
on E: EIdException do begin
Reconnect;
end;
end;
procedure Reconnect;
var
k: Integer;
begin
IdTCPClient.Disconnect;
if IdTCPClient.IOHandler <> nil then
IdTCPClient.IOHandler.InputBuffer.Clear;
for k := 0 to 4 do
begin
Sleep(1000);
try
IdTCPClient.Connect;
Exit;
except
on E: Exception do
begin
MessageDlg('Connecting Error: '+E.Message, mtError, [mbOk], 0);
if k = 4 then
raise;
end;
end;
end;
end;
before you connect make sure that the passive Boolean of idftp is false
when you need file transfert change it to true with binary file option
Related
I'm using Delphi 10 Seattle to build a simple Client/Server application using the TIdTCPClientand TIdTCPServer components.
For read the data arrived from the server application (TIdTCPServer) I'm using a Thread in the Client Application.
This is the Execute method
procedure TClientReadThread.Execute;
begin
while not Terminated do
begin
try
if FClient.Connected() then //FClient is TIdTCPClient
begin
if not FClient.IOHandler.InputBufferIsEmpty then
begin
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
end
else
FClient.IOHandler.CheckForDataOnSource(10);
end;
except
on E: Exception do
begin
// Send the exception message to the logger
FE:=E;
Synchronize(LogException);
end;
end;
end;
end;
Under normal circumstances all is working fine, but now I'm doing some tests to restore the connection on the client application in case which the server or the network is down. So I shutdown the server App to simulate a issue when the comm fails.
When that happens the client application detects which the server is gone using the TIdTCPClient.OnStatus event.
After that I try to terminate the reading thread using this code
if Assigned(FClientReadThr) then
begin
FClientReadThr.Terminate;
FClientReadThr.WaitFor; // This never returns.
FreeAndNil(FClientReadThr);
end;
But the WaitFor function never returns.
SO the question is , there is something wrong on my execute procedure which is preventing the finalization of the thread?
Exist a better way to terminate the thread?
First, you should not be using Connected() in this manner. Just call ReadLn() unconditionally and let it raise an exception if an error/disconnect occurs:
procedure TClientReadThread.Execute;
begin
while not Terminated do
begin
try
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
except
// ...
end;
end;
end;
If you want to poll the socket for data manually, it should look more like this:
procedure TClientReadThread.Execute;
begin
while not Terminated do
begin
try
if FClient.IOHandler.InputBufferIsEmpty then
begin
FClient.IOHandler.CheckForDataOnSource(10);
FClient.IOHandler.CheckForDisconnect;
if FClient.IOHandler.InputBufferIsEmpty then Continue;
end;
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
except
// ...
end;
end;
end;
DO NOT use the TIdTCPClient.OnStatus event to detect a disconnect in this situation. You are deadlocking your code if you are terminating the thread directly in the OnStatus event handler. That event will be called in the context of the thread, since the thread is the one reading the connection and detecting the disconnect. So your thread ends up waiting on itself, that is why WaitFor() does not exit.
I would suggest an alternative approach. DON'T terminate the thread at all. To recover the connection, add another level of looping to the thread and let it detect the disconnect and reconnect automatically:
procedure TClientReadThread.Execute;
var
I: Integer;
begin
while not Terminated do
begin
try
// don't call Connect() in the main thread anymore, do it here instead
FClient.Connect;
except
// Send the exception message to the logger
// you should wait a few seconds before attempting to reconnect,
// don't flood the network with connection requests...
for I := 1 to 5 do
begin
if Terminated then Exit;
Sleep(1000);
end;
Continue;
end;
try
try
while not Terminated do
begin
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
end;
except
// Send the exception message to the logger
end;
finally
FClient.Disconnect;
end;
end;
end;
You can then Terminate() and WaitFor() the thread normally when you want to stop using your socket I/O.
I am using indy TIDHTTP to code a way to know whether my server on the internet is down or the address of the page on the same server is not available.
I copied the suggestion given in another thread on stackoverflow:
try
IdHTTP1.Get(mypage_address);
except
on E: EIdHTTPProtocolException do begin
if e.errorcode=404 then
showmessage('404 File not found');
// use E.ErrorCode, E.Message, and E.ErrorMessage as needed...
end;
end;
but this way I am only able to detect a server response code and not whether the server did not respond at all. I guess it's trivial but I do not know what is the way to do that?
An EIdHTTPProtocolException exception is raised when TIdHTTP successfully sends a request to the server and it sends an HTTP error reply back to TIdHTTP. If the server cannot be reached at all, a different exception (typically EIdSocketError, EIdConnectException, or EIdConnectTimeout) will be raised instead.
try
IdHTTP1.Head(mypage_address);
except
on E: EIdHTTPProtocolException do begin
ShowMessage(Format('HTTP Error: %d %s', [E.ErrorCode, E.Message]));
end;
on E: EIdConnectTimeout do begin
ShowMessage('Timeout trying to connect');
end;
on E: EIdSocketError do begin
ShowMessage(Format('Socket Error: %d %s', [E.LastError, E.Message]));
end;
on E: Exception do begin
ShowMessage(Format('Error: [%s] %s', [E.ClassName, E.Message]));
end;
end;
I attempted doing the server/site checking scientifically. but eventually simply came down to this:
function TFrameSiteChecker.GetSiteHeader(const AUrl: string): Integer;
begin
try
idhttp1.Head(AUrl);
Result := idhttp1.ResponseCode;
except
on E: exception do
Result := 0;
end;
end;
Logic being, getting the head reduces traffic, log sizes etc.
There is one one right result from the function - the return of status code 200, anything else is a fail.
Also I failed to force windows / the system / indy to not buffer/cache content, so also eventually, just run the checker every 30 minutes on a schedule. Otherwise (unless something else clears the cache) after the first connect it always succeeds, even if you unplug the machine from the network!
Delphi version : XE2,
Indy version: 10.5.8.0.
I have three procedures and all work fine until internet connection gets lost. When it will happen and after that I will try sending message then I can't reconnect when internet will be back. Can't close program (after on close program be not visible, but will use 100 cpu usage). Without "try, exception" there is a Socket Error #1053 on IdIRC1.Say and on Close. Thanks for help.
///Connection:
procedure TForm1.Button5Click(Sender : TObject);
begin
try
IdIRC1.Nickname := 'zzz';
IdIRC1.Password := 'kkk';
if IdIRC1.Connected then
IdIRC1.Disconnect;
IdIRC1.Connect;
IdIRC1.Join('#' + edit3.Text);
except
ShowMessage('ggg');
end;
end;
///Send message:
procedure TForm1.Button3Click(Sender : TObject);
begin
try
IdIRC1.Say('#' + edit3.Text, edit2.Text);
if (edit2.Text <> '') and (IdIRC1.Connected) then
begin
memo6.Lines.Add(edit2.Text);
Edit2.Clear;
end
else
ShowMessage('xxx');
except
ShowMessage('yyy');
end;
end;
///On close:
try
IdIRC1.Disconnect;
except
end;
When you encounter an error accessing the connection, such as because the connection was lost, you need to call Disconnect() and you need to clear the IOHandler.InputBuffer if it still has unread data in it. Disconnect() does not clear the InputBuffer, by design. If the InputBuffer is not empty, Connected() will return True even if the physical socket is disconnected.
I have a TADOConnection inside a thread. In the event that it fails to connect to the database (timeout), when closing the app, the thread is held up and it takes time until the attempt is finished before my app is able to close. I don't want to reduce the connection timeout of the connection, this isn't the issue. Is there any way I can forcefully abort the attempt to connect?
The TADOConnection connects at the beginning of the thread execution and automatically reconnects repeatedly until success. Then, upon closing the app, if the database is failing to connect, the thread hangs until the connection attempt is finished (timed out).
EDIT
This is a sample of how the thread works:
procedure TMyThread.Init;
begin
CoInitialize(nil);
FDB:= TADOConnection.Create(nil);
FDB.LoginPrompt:= False;
FDB.ConnectionTimeout:= 5;
FDB.ConnectOptions:= coAsyncConnect;
end;
procedure TMyThread.Uninit;
begin
if FDB.Connected then
FDB.Connected:= False;
FDB.Free;
CoUninitialize;
end;
function TMyThread.Reconnect: Boolean;
begin
Result:= False;
if FDB.Connected then
FDB.Connected:= False;
FDB.ConnectionString:= FConnectionString;
try
FDB.Connected:= True; //How to abort?
Result:= True;
except
on e: exception do begin
//MessageDlg(e.Message, mtError, [mbOK], 0);
FDB.Connected:= False;
Result:= False;
end;
end;
end;
procedure TMyThread.Process;
begin
if Reconnect then begin //Once connected, keep alive in loop
while FActive do begin
if Terminated then Break;
if not Connected then Break;
//Do Some Database Work
end;
end else begin
//Log connection failure
end;
end;
procedure TMyThread.Execute;
begin
while not Terminated do begin
if FActive then begin
Init; //CoInitialize, create DB, etc.
try
while (FActive) and (not Terminated) do begin
try
Process; //Actual processing procedure
except
on e: exception do begin
//Record error to log
end;
end;
end;
finally
Uninit; //CoUninitialize, destroy DB, etc.
end;
end;
end;
end;
(Tried to include just relevant things to the question)
First thing that comes to mind is to reduce connection's timeout. Why do you not want that? And why do you want to establish a connection when closing the application? Especially when you prefer to abort it when it takes more time than expected, why connect at all? Sounds like we could know more background info.
In the special case that you really need it on the condition that it connects quickly, ánd when this issue only applies to application's destruction, then I suggest not to wait for the thread to finish. Just do not free it, terminate the application, and let Windows kill the process including all its threads.
In the case that the connection does succeed, then this approach could backfire, so signal your main thread when the thread dóes connect, and postpone its termination by yet waiting for the thread. You may need another timeout for that again.
Edit:
I suppose the OnWillConnect event will occur every time the attempt to connect is made. Try returning EventStatus := esCancel within its handler.
How should I be recovering in this situation?
The server crashes, thus the connection has been abnormally closed. Calls to almost everything result in "Connection Reset By Peer" exceptions. I seem to have fixed it by calling Disconnect on the TIdTCPClient object inside the except block, but it results in one final exception with the same message (which I have caught in the second try-except block).
This is with Indy10 and Delphi XE2.
try
if not EcomSocket.Connected then EcomSocket.Connect();
except
on e: Exception do begin
try
EcomSocket.Disconnect();
except
MessageDlg('Connectivity to the server has been lost.', mtError, [mbOK], 0);
end;
end;
end;
Try this:
try
if not EcomSocket.Connected then EcomSocket.Connect();
except
try
EcomSocket.Disconnect(False);
except
end;
if EcomSocket.IOHandler <> nil then EcomSocket.IOHandler.InputBuffer.Clear;
MessageDlg('Connectivity to the server has been lost.', mtError, [mbOK], 0);
end;