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

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.

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;

Imap4 client command LSUB

I have a problem with function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean; with this implementation :
function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean;
begin
{CC2: This is one of the few cases where the server can return only "OK completed"
meaning that the user has no subscribed mailboxes.}
Result := False;
CheckConnectionState([csAuthenticated, csSelected]);
SendCmd(NewCmdCounter, IMAP4Commands[cmdLSub] + ' "" *',
[IMAP4Commands[cmdList], IMAP4Commands[cmdLSub]]); {Do not Localize}
if LastCmdResult.Code = IMAP_OK then begin
// ds - fixed bug # 506026
ParseLSubResult(AMailBoxList, LastCmdResult.Text);
Result := True;
end;
end;
When I debug I see that the LastCmdResult.Text stringlist is empty, but the LastCmdResult.FormattedReply stringlist has all folders on my email server (Inbox, Sent, Trash, ...). When I tried to use LastCmdResult.FormattedReply count or text, it had immediately lost its data and gave LastCmdResult.FormattedReply.Count=0 and LastCmdResult.FormattedReply.Text=''. So I'd like to know if there is a way to enter the data inside LastCmdResult.FormattedReply and get my email server folders or there is another way to solve my problem ?
I have a problem with function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean; with this implementation :
Works fine for me when I try it using the latest SVN version of Indy.
When I debug I see that the LastCmdResult.Text stringlist is empty, but the LastCmdResult.FormattedReply stringlist has all folders on my email server (Inbox, Sent, Trash, ...).
When I run it, the opposite happens. LastCmdResult.Text contains the expected text, and LastCmdResult.FFormattedReply is empty (notice I mention the FFormattedReply data member directly, see below).
When I tried to use LastCmdResult.FormattedReply count or text, it had immediately lost its data and gave LastCmdResult.FormattedReply.Count=0 and LastCmdResult.FormattedReply.Text=''.
That is by design. The FormattedReply property is intended to be used by a client to parse a server reply so it can populate TIdReply's property values, and to be used by a server to generate a new reply using TIdReply's property values. So, you cannot read from the FormattedReply property on the client side.
So I'd like to know if there is a way to enter the data inside LastCmdResult.FormattedReply and get my email server folders or there is another way to solve my problem ?
The whole purpose of ListSubscribedMailBoxes() is to return the folder names in the AMailBoxList parameter. If that is not working for you, then either
you are using a older/buggy version of Indy.
your server is sending the data in a format that TIdIMAP4 is not able to parse.
Without knowing which version of Indy you are actually using, or what the server's reply data actually looks like, there is no way to diagnose your issue one way or the other.

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.

Synapse TBlockSocket occasionally freezes

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.

Error: 536 Error in File <report filename> Unable to connect: incorrect log on parameters

I am getting the above error on a client's machine when executing the following code
for i := 0 to crpe.Subreports.Count - 1 do
for j:=0 to crpe.Subreports[i].Tables.Count - 1 do
crpe.Subreports[i].Tables[j].Path := path;
crpe.Subreports[0]; {This line points the VCL back to the main Report}
for i := 0 to crpe.Tables.Count - 1 do
crpe.Tables[i].Path := path;
The path and the tables exist. The above code also fails when connecting to a report with no subreports.
I cannot see why it is given me incorrect log on parameters as they are just tables. On other machines it works just fine. Any ideas?
Try removing the fourth line
(crpe.Subreports[0]; {This line points the VCL back to the main Report} )
Try it first on a machine where your program seems to be working, to see if you really need the line.
If that works, then try it on the client's machine to see if it makes a difference.
That line of code does not make any sense. It just references an object, but does not assign it or use it. Why isn't it causing a compiler error?
-Al.

Resources