Synapse TBlockSocket occasionally freezes - delphi

I've createdan application which communicates with external device using TCP/IP as a client. I'm using Synapse library (v40) for communication. Sometimes however communication freezes. I managed to get callstack with JclDebug,showing that despite defined timeout, receiving packets is the problem.
Delphi 2009 is used.
Is there anything I can do to fix this issue? Bug in Synapse?
[77297094] KiFastSystemCallRet
[006193FE] blcksock.TBlockSocket.InternalCanRead (Line 2741, "synapse\blcksock.pas")
[0061945C] blcksock.TBlockSocket.CanRead (Line 2764, "synapse\blcksock.pas")
[006185E5] blcksock.TBlockSocket.RecvPacket (Line 2324, "synapse\blcksock.pas")
[0061888F] blcksock.TBlockSocket.RecvTerminated (Line 2410, "synapse\blcksock.pas")
... my own code..
Edit: The blocking line is:
x := synsock.Select(FSocket + 1, #FDSet, nil, nil, TimeVal);
Select -function is from winsock2 API.
Edit2: TimeVal is set by Synapse code:
var
TimeVal: PTimeVal;
TimeV: TTimeVal;
..
TimeV.tv_usec := (Timeout mod 1000) * 1000;
TimeV.tv_sec := Timeout div 1000;
TimeVal := #TimeV;
if Timeout = -1 then
TimeVal := nil;
Original source code is here: http://synalist.svn.sourceforge.net/viewvc/synalist/trunk/blcksock.pas?revision=154&view=markup
Timeout used is 1000.
Edit3: I've two client threads running to communicate with two different hosts. It looks like only other one is hanging. Application has been running now since thursday. Thread #2 hung after 5 hours, but thread #1 is still running.

As I couldn't find the reason for the freeze, I changed by code a bit and now end up calling RecvTerminated with CRLF as terminater instead of '>', and it seems to work without stopping.

Related

How to use and stabilize IdHttp for a large number of sites

I have about 30 unique sites which I login to and download a few files from each one of them. Sometimes I have about 50 sites, which the 20 extra are the same as previous ones but with different login credentials.
If I run the download process for any of them with all the other sites disabled, all of them work great. But if I try to download them one after another, I usually get about 5-10 errors from 40 sites!
Exceptions that occur are usually socket error, connection closed gracefully or unknown error occured!
Right now, I create a class for each site, and in each class I create a TIdHTTP or TIdFTP object (very few are FTP and I don't have a problem with any of them). When the download from one class is finished, I destroy the class and destroy the TIdHTTP, and start the download process for the next class (or site), so the downloads are not parallel. But I do create about 40 TIdHTTP and a few TIdFTP objects at the very beginning, and I start the download process one after another.
Is my approach correct? Or should I use only one TIdHTTP object in all classes? But if I have to do so, how can I refresh or reset it? Sometimes I have to login to a single site multiple times with different credentials.
I should also mention that one approach I came up with that did help a little bit (maybe solved one or two errors!) was this:
// mMaxTryCount is 4 and mSleepInterval is 1000
for I := 1 to mMaxTryCount do
begin
if not(isSuccess) then
begin
if (i = mMaxTryCount) then
begin
responseCode.Clear;
idHttp.Post(URL, requestList, responseCode);
end
else
try
responseCode.Clear;
idHttp.Post(URL, requestList, responseCode);
isSuccess := true;
except
Sleep(mSleepInterval * (i + (i - 1)));
end;
end;
end;

WinHttpWriteData seems to be "flooding" server

I'm using WinHttpSendRequest/WinHttpWriteData to upload a large (54Mb) file to our server, sending it in 4Kb lumps to give user feedback. This has been working well, as far as I know, until recently. Now, when I try it, the upload goes very quickly and then the WinHttpReceiveResponse() call times-out and incomplete data is received by the server.
I'm using Win 8.1 64bit, IE11 11.0.15 (I think that WinHttp is updated with IE) but on my colleague's PC - same version of Windows, IE, the upload is much slower and the response doesn't time-out. When I try testing on various virtual machines, the problem isn't apparent. Other colleagues, however ... oh Windows!!
Just to be clear
As far as I am aware, this code used to work!
WinHttpOpen is called without the ASYNC flag set.
The HTTP verb is POST
The code in Delphi XE2
Result:= WinHttpSendRequest(RequestHandle,PWideChar(Headers),Length(Headers),WINHTTP_NO_REQUEST_DATA,0,FormBuffer.Size,Cardinal(Self));
If Result
then begin
BytesToWrite:= FormBuffer.Size;
while BytesToWrite > 0
do begin
If BytesToWrite > SizeOf(WriteBuffer)
then BufFill:= SizeOf(WriteBuffer)
else BufFill:= BytesToWrite;
FormBuffer.ReadBytes(WriteBuffer,BufFill); // FormBuffer is my object to supply data and headers
If WinHttpWriteData(RequestHandle,#WriteBuffer[0],BufFill,Written)
then Dec(BytesToWrite,Written)
else Error('WinHttpWriteData'); // Error() method calls GetLastError, assembles error message and logs it
If Assigned(OnDataWrite)
then OnDataWrite(Self,Written); // Event that notifies user
end;
FetchResponse(RequestHandle); // Calls WinHttpReceiveResponse() and then fetches data
Result:= True;
end
else GLE:= Error('WinHttpSendRequest');
This code was largely an adaptation of this code:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384120(v=vs.85).aspx
The "WinHttp Sample code to do a PUT." at the bottom.
It's AVG ...!
Disabling AVG gives normal performance for the upload ... now it's just a matter of finding out which part(s) are getting in the way.

CreateFileMapping - ERROR_NOT_ENOUGH_MEMORY Delphi XE7

I have the folowing "general meta" problem.
I will describe what I remember of doing.
I work with Delphi XE7.
Yesterday all my code function corect.
a. I compiled 2 programs deployed at clients an work ok.
After that I installed (if I remember corect) one package from CleverComponents to compare firebird database.
After open some samples I decided to postpone the samples and get back to 1 program to fix a problem that i received.
the program suddenly does not work anymore.
The component that is problematic is TclDownLoader from Clever Internet Suite.
when I use the TclDownLoader to download something, the component enter in the following code:
FMapHandle := CreateFileMapping(AFileHandle, nil, p, h, l, nil);
if (FMapHandle = 0) then begin
raise EclStreamError.Create('TODO ' + IntToStr(clGetLastError()));
end;
the error = 8; (message is show TODO 8);
After several trying to clean up dcu, reinstall the Internet suite, delete newest instaled package, clear any trace of what I installed I dont hane any ideeas left.
I'm sure that there is no code problem only some "colision" of the version or who knows.
Any ideea on how to fix this problem?

Reading from serial port iis erratic without user input

Using Delphi 7 I am reading from a serial port.
The read is always preceded by a write which triggers the h/w to measure from a sensor and write something for me to read (and there is always something to read).
I have two possibilities: manually enter a command and click a button to write that to the serial port (read model or f/w version, etc) or click a button to loop reading measurements until a stop button is pressed. These both use the same internal functions, so the code looks something like this:
WriteSerial('?model');
SerialData := ReadSerial(); // returns string
WriteSerial('?fw');
SerialData := ReadSerial();
and
while stopButtonNotPressed do
begin
WriteSerial('?data');
SerialData := ReadSerial();
Memo1.Lines.Add(SerialData );
end;
The first variant (manually entering a command & pressing a button) is always successful, no matter how quickly or slowly I enter commands (hold down button for repeat), where are the second goes
pass
fail
pass
pass
fail
pass
pass
fail
... add infinitum
adding calls to sleep produces nothing, but trying to debug, I found that if I add a modal dialog box MsgDialog, 'Please close this dialog...', mtInfo, [mrOK]); to the loop, then it no longer fails.
Now, it doesn't look like timing (else surely adding Sleep(2000); to the loop would make it pass & does not, so why does pressing a button on the main form or the modal dialog cause it to succeed?
Btw, the h/w user guide says nothing of CTS / RTS, and the sole code example provide also does not.
Note: if I manually enter ?data repeatedly it never fails ...
Any ideas?
Your serial devices need time to react, so obviously you need a break for the device to catch up. When you use the keyboard to push the button you're providing the brake it needs because the keyboard repeat isn't all that fast.
As you say Sleep(2000) should provide plenty of "break", but there are two other potential problems you'll need to take care of:
Serial communication isn't necessary buffered: Sleep(2000) might be too long!
The serial library you're using might be using windows messages to process incoming bytes. Sleep() inhibits the message pump, so no more messages flow towords your application
Try "sleeping" using something like this:
procedure BusyWait(ms: Cardinal);
var StopAt: TDateTime;
begin
StopAt := Now + EncodeTime(0, 0, ms div 1000, ms mod 1000);
while StopAt > Now do
begin
Application.ProcessMessages;
Sleep(50); // per MichaƂ Niklas's suggestion, to keep the CPU from reaching 100%
end;
end;
This routine will wait, but it'll keep the message pump going, allowing your serial library to receive messages. If that's the problem...
Maybe adding Application.ProcessMessages() before Sleep() will help.

Counting connections and commands in a simulator (Delphi 7/Windows XP)

I was working in a simulator for testing connections and commands sending to a server. The simulator has some counters like Total of sent commands, successfully sent commands, fail sent commands, connection attempts, successful connections, etc...
The code that I used is the following:
procedure TALClient.SendCommand;
begin
Try
dlgMain.IncrementIntConx; //Increments conn attemps
FTCP.Connect(1000);
If FTCP.Connected Then
Begin
dlgMain.IncrementConections; //increments successfully connections
try
dlgMain.IncrementIntSendCommand; //Increments command sent attemps (A)
FTCP.SendCmd(FCmd.FNemo + ' ' + FCmd.FParams); // (Z)
dlgMain.IncrementSendComm; //Increments sent Commands (B)
try
FParent.CS.Acquire;
FParent.FStatistic[Tag, FCmd.FTag].LastCodeResult := FTCP.LastCmdResult.NumericCode;
FParent.FStatistic[Tag, FCmd.FTag].LastMsgResult := FTCP.LastCmdResult.Text.Text;
FParent.CS.Release;
if ((FTCP.LastCmdResult.NumericCode) = (497)) then
Synchronize(UpdateCorrectCounters) //increments successfully responds from server
else
Synchronize(UpdateErrorCounters); //increments failed responds from server
except
Synchronize(UpdateErrorCounters);
end;
except
dlgMain.IncrementFailCommand; //increments failed commands (C)
end;
End
Else
Synchronize(UpdateErrorCounters); //Increment failed responses from sever
Finally
If FTCP.Connected Then
FTCP.Disconnect;
End
end;
I have changed the code to many many other ways, but it never works fine.
The big problem is that the total count of sent commands is not equal to successfully sent commands plus failed sent commands. (in the code: A is not equal to B plus C). There are responses that I have never "seen" in the line marked as (Z), maybe "lost" responses...
So, what I am doing wrong?
I guess you are using multiple threads for your simulator. This looks like the classic Lost Updates problem to me. You have to synchronize the counter-incrementing code.
Incrementing a variable is NOT thread-safe:
Temp := CounterValue;
// If another thread intercepts here, we've got a lost update
Temp := Temp + 1;
CounterValue := Temp;
See this MSDN article to read more about concurrency issues.
If you only use counting you can use the Windows functions InterlockedIncrement and InterlockedDecrement and you won't need any locking.

Resources