My app sends data to a Server using a TidTCPClient. The Server uses a TidTCPServer. All works fine and I am now trying to handle unexpected situations.
When I disconnect the network cable between server and client, and then try to close the client, it waits for a long time, until it finally closes:
TCPClient.IOHandler.InputBuffer.Clear;
TCPClient.Disconnect;
TCPClient.Free;
TCPClient.Free is the place where it waits. This is sometimes 30 seconds, sometimes even longer.
Is there a way to terminate a TCPClient immediately, no matter what it is doing at that moment?
You should be clearing the InputBuffer after calling Disconnect(), not before.
In any case, there is no reason for a socket to cause such a hang when disconnecting/freeing the TIdTCPClient, unless you have messed around with the socket's LINGER options. So I have to suspect your own code is at fault first. Which version of Indy are you using? Do you have any event handlers assigned to the TIdTCPClient? Have you tried stepping through TIdTCPClient's destructor in the debugger to see what is really happening?
It probably waits for a timeout. Look at the properties of the component to lower the timeout value
Related
I use TIdHTTPServer in Delphi 11 to run a simple web server on a VPS.
It works great, except from time to time my app will start to use 100% of the CPU and keep this way forever, and I can't identify what is causing this.
When this happens, the server is still active and replying to requests, but very slowly. The only way to fix this is to force close the application and open it again.
I don't have any code to show, as my code is just generic responses using the OnCommandGet event of the TIdHTTPServer. This event will handle GET parameters on the URL and return something in the AResponseInfo.ContentText.
I know this is difficult, but any ideas about what I should hunt for to fix this?
We use TIdHttpServer quite a lot and have no problems with it. We use it in Delphi 10.3-10.4.2, but it’s not the reason for the problem. Programs work a few months without restarting.
From our experience we can say that problem of such unexpected behavior can be (in order of probability):
Code is not threadsafe. Event OnCommandGet run not in a main thread, so all access to global object/resources/etc must be done thru some kind of synchronization mechanism (locks, TEvent, synchronize, mutex, semaphore or other). If code does not use synchronization – it can broke logic, throw exceptions or do some other unexpected actions (like high CPU usage).
Connections count go over the limit. TIdHttpServer has properties like ListenQueue and MaxConnections. Can be that you make more requests that the server can handle. In this case your new requests wait until they can be handled by your code and it can make some additional CPU usage. To diagnose this – try to increment some internal variable at the start of your event and decrement it at the end. Make some service request to return this variable and you will know if all work correctly. Other similar situation – connection does not close after using the inside event and stay in memory, then you can go over limits too. Try to workaround properties TIdHttpServer.KeepAlive := false and TIdHttpServer.ReuseSocket := rsFalse.
Memory leaks. Try to set variable ReportMemoryLeaksOnShutdown := true and start the application, make a few requests and close it. If you’ll see a message with leaks – then you do something wrong, try to handle these objects in the right way. In production these small leaks can take a lot of RAM and Windows will dump part of memory into a swap-file, so your new requests will take more time to be processed.
Without an example, we can't say more.
I downloaded some interesting code from Indy 10 TIdTCPServer and compiled and ran it. It's a bit messy and it probably needs work but it's a good foundation for something interesting. At least I don't have to post all the code here. Anyway, I decided that the ExampleServer needs to be de-activated first, not just freed in OnDestroy. So I added:
procedure TServerPushExampleForm.FormClose(Sender: TObject);
begin
ExampleServer.Active := False; // bug here - never returns
end;
To try to debug this, I added IdCustomTCPServer.pas to the project and stepped into it.
An exception is raised in TIdCustomTCPServer.StopListening; at the line LListener.WaitFor; This exception is trapped in the exception handler in TIdListenerThread.Run; but the E.message isn't retrieved there, so I had to modify the code there to get the message:
"Operation Aborted"
I traced it further after that, but the code execution eventually comes back to the same exception handler.
Is this a bug or is it just better to never set the Active property to False? In my tests, if I close the app and let the RTL manage all the free'ing. The infinite loop doesn't occur (the app does actually close)
The exception is normal behavior.
Indy uses blocking sockets.
TIdListenerThread runs a loop waiting for clients to connect. Each wait is a blocking operation.
When the server is being deactivated, it closes its listening socket(s), causing pending socket operations to abort, and then the listening thread(s) are terminated. The abort exception is handled internally.
The server's destructor also sets Active=False, so whether you deactivate explicitly or not, the server will be deactivated.
This, in if itself, is not causing your hang. Typically, the only way setting Active=False can ever hang is if a server event handler tries to sync with the main UI thread synchronously while the main thread is blocked waiting for the server to deactivate. Classic deadlock scenario.
That does not appear to be the case in the demo you linked to, though (the only sync being used is asynchronous instead). So, something else is likely going on that your debugging hasn't reveiled yet. There should be no hang.
I use Delphi 10.2 Tokyo with Indy (the integrated version).
Scenario:
I do http GET requests (TIdHttp) in threads, and use proxies.
Sometimes, a proxy seems to cause Indy not to timeout, and not to return from a GET.
I also assign the onWork event handler to react to an Abort button and call within this handler the idHttp.Disconnect function. When the GET seems to be frozen, the abort also does not work, possible that in this case the onWork event is not triggered, I have no idea.
The main thread is idle and only create lets say 50 threads, each does a GET via its instance of TIdHttp. Ans sometiumes, as I mentioend, a proxy cause of GET not to return which result then in a "hanging" thread.
My question is: How can I force Indy to abort, from an external thread? Is there anything what can be done via code when the GET refuse to return?
I solved my issues by using a background thread to disconnect sockets and implement a timeout, which seems to work even socket are "frozen" and the onWork is not triggered.
I do this by adding the TIdHttp instances I create to an array, together with the time the instance was created. If the GET return normal, the array entry will be removed. In a background thread, I check if the user clicked Abort, and then loop through the array and call disconnect on each instance. I also check in the same thread if a timeout period was reached, and also call disconnect.
Might not be the perfect solution, but it works for me.
I have trouble with Delphi XE2 app. Sometimes WinInet call to ASMX service blocks and never returns - user must terminate process from task manager to close app.
To connect to ASMX service app uses service generated by WSDLImp tool.
During its work, app makes a lot of calls to web service (~1000-2000). And at some moment (last time it was 782 request item, first time it was near the end) app freezes. After some digging, logging I find out that app blocks on
WinInetResult := HttpSendRequest(Request, nil, 0, DatStr.Bytes, DatStr.Size);
In Soap.SOAPHTTPTrans unit
First guess was it is some server-side problem – server hangs on request processing. But on trials server was processing requests from other clients, while target one was blocked. And, when you use Fiddler to debug http traffic from app everything works as expected, no locks. Also, WinInet’s SendTimeout, ReceiveTimeout, ConnectTimeout has no effect – there are no timeout errors. One more point, app blocks not on specific method call, but on different ones.
After googling, I find out that HttpSendRequest can block on max parallel connections exceeded. But there are no parallel execution in app – each action is performed in main GUI thread.
My next try was to use Indy for HTTP communication instead of WinInet. And with Indy, app does its work as it should, no locks. But downside is performance degradation – app’s work takes two times longer with Indy.
This is not very good. So, I want to go back to WinInet. But for this I need to find reason of blocking. Does anybody know why HttpSendRequest can block?
P.S.
It is strange that with Indy we have such performance degradation. Maybe there are some properties, parameters to increase performance?
So, I have finally fixed this issue.
After all trials with no success, I've re-implemented SOAP calls using WinHTTP instead of WinInet.
With WinHTTP everything works normally.
We use TIdSMTPRelay to relay some messages. Sometimes the receiving email server will hang and just keep the connection open and our relayer never times out. Is there a way of giving it e.g. 5 mins to deliver the message and then give up?
TIdSMTPRelay has ConnectTimeout and ReadTimeout properties. Indy does not have a SendTimeout property, so if the hang is occurring during a send rather than a connect/read, the only option would be to manually assign an SO_SNDTIMEO timeout on the underlying socket itself, such as by using the Socket.Binding.SetSocketOption() method in the OnConnect event.