Delphi: Limiting TCP connections - delphi

I'm using the TServerSocket component in my Delphi application. I would like to limit client connections, let's say to one thousand. Unfortunately i don't know how to do that. The component does not provide any properties like 'MaxConnections' or any other like this one.
I create new server threads (TServerClientThread) using the OnGetThread event. To get the number of active connections I used the 'Socket.ActiveConnections' property.
Unfortunately I don't know what to do then. I tried not to create any thread inside the OnGetThread procedure when the number of connections is above the limit, but it changed nothing - client, even though it is unable to send or receive any information, it can connect to the server and stay connected. What to do not to allow new clients to connect or just allow them to connect but break the connection instantly?

Last time i used Delphi was some years ago, but i had a similar situation to deal with and my experience could be useful for you: i was facing the same problem and didn't want to switch to the "Indy" components since the (big) application wasn't worth the port.
As far as i can remember, you should have an onClientConnect event on the server socket and here is were i checked for the limit:
.onClientConnect( Sender: TObject; aSocket: T... )
begin
if( YourServerSocket.ActiveConnections > YourDefinedMaxConnections )
begin
// Drop the connection
aSocket.Close;
end;
end
I can't remember more other than that but i think i did something on these lines, or at least this was the thing i came up with.

Related

Auto refresh a TDataSet / DBGrid

I'm developing a software that displays information in a DBGrid via a TSimpleDataSet (dbExpress components)
The software in question is used on 2 different computers by 2 different people.
They both view and edit the same information at different times.
I'm trying to figure out a way to automatically update the DBGrid (or rather, the DataSet, right?) on Computer B once Computer A makes a change to a row (edits something/whatever) and vice-versa.
Currently I've set up a TButton named Refresh that once clicked executes the following code:
procedure TForm2.actRefreshDataExecute(Sender: TObject);
begin
dbmodule.somenameDataSet.MergeChangeLog;
dbmodule.somenameDataSet.ApplyUpdates(-1);
dbmodule.somenameDataSet.Refresh;
dbmodule.somename1DataSet.MergeChangeLog;
dbmodule.somename1DataSet.ApplyUpdates(-1);
dbmodule.somename1DataSet.Refresh;
dbmodule.somename2DataSet.MergeChangeLog;
dbmodule.somename2DataSet.ApplyUpdates(-1);
dbmodule.somename2DataSet.Refresh;
dbmodule.somename3DataSet.MergeChangeLog;
dbmodule.somename3DataSet.ApplyUpdates(-1);
dbmodule.somename3DataSet.Refresh;
end;
This is fine and works as intended, once clicked.
I'd like an auto update feature for this, for example when Computer A edits information in a row, Computer B's DBGrid should update it's display accordingly, without the need to click the refresh button.
I figured I would use a TTimer and set it at a specific interval, on both software on both PC's.
My actual question is:
Is there a better way than a TTimer for this? If so, please elaborate.
Also, if the TTimer route is the way to go any further info you might find useful to state would be appreciated (pro's and con's and so on)
I'm using Rad Studio 10 Seattle and dbExpress components, the datasets connect to a MySQL database on my hosting where my website is.
Thanks!
Well, Ken White and Sertac Akyuz are certainly correct that using a server-originated notification to determine when to refresh your local dataset is preferable to continually re-reading all the data you are using from the server.
The problem AFAIK is that there is no Emba-supplied notification system which works with MySql. See this list of databases supported by FireDAC's Database Alerts:
http://docwiki.embarcadero.com/RADStudio/XE8/en/Database_Alerts_(FireDAC)
and note that it does not list MySql.
Luckily, I think there is a work-around which should be viable for a v. small system like yours currently is. As I understand it, you and your colleague's PCs are on a LAN and the MySql Server is outside your LAN and on the internet. In that situation, it doesn't need a round trip to the server for one of you to get a notification that the other has changed something in the database. Using an analogy akin to Ken's, you can, as it were, lean over the desk and say to your colleague "Hey, I've changed something, so you need to refresh your data."
A very low-tech way of implementing that would be to have somewhere on your LAN a resource that both of you can easily get at, which you can update when you make a change to the DB that means that the other of you should update your data from the server. One way to do that is to have a small, shared datafile with a number of records in it, one per server db table, which has some sort of timestamp or version-ID number which gets updated when you update the corresponding server table. Then, you can periodically check (poll) this datafile to see whether a given table has changed since you last checked; obviously, if it has, you then re-read the data you want from it from the server and update your local record of the info you read from the shared file.
You can update the shared file using handlers for the events of your Delphi client-side datasets.
There are a number of variations on this theme that I'm sure will be apparent to you; the implementational details really don't matter.
To update the shared file I'm talking about, you will need to lock it while writing to it. This answer:
How do I get the handle for locking a file in Delphi?
will show you how to do that.
Of course, the shared local resource doesn't have to be a data file. One alternative would be to use a Microsoft Message Queue service, which is sometimes used for this kind of thing, but has a steeper learning curve than a shared data file.
By the way, this kind of thing is far easier to do (at least on a small scale like you have) if you use 3-tier database access (e.g. using datasnap).
In a three tier system, only the middle tier (a Delphi datasnap server which you write, but it's not that hard) talks to the server, and the clients only talk to the middle tier. This makes it easy for the middle tier server to notify the other client(s) when one of them changes the db data.
The three-tier arrangement also helps minimise the security problems with accessing a database server via the internet, because you only need one secure connection to the server, not one per client. But that's straying a bit far from your immediate problem.
I hope all this is clear, if not, ask.
Just use a timer and make it refresh the dataset every 5 min. No big deal.
If the usage is not frequent then you can set it to fire every 10 or 15 min.
There is nothing wrong with the timer if it set on longer intervals.
Today's broadband connection's can easily handle the traffic so can Access.
If the table is not huge of course.

Firebird ISC ERROR CODE:335544721

We have had this error recently reported by our clients and we on't know how we can fix this. We are using Delphi XE6 to develop our application and it connect to Firebird (v2.5) database as back-end. We also have used IBObjects for working with Firebird database in Delphi.
We have managed to replicate the error on the dev machine by stopping Firebird windows service before running a query but we haven't found a way to detect the connection lost in the code (e.g. by having an event) so as we don't know when this happens so we cannot reconnect to the database in the code either.
By the way, this is full error message if it helps:
ISC ERROR CODE:335544721
ISC ERROR MESSAGE:
Unable to complete network request to host "BON-VFS-01".
Error writing data to the connection.
Any help really appreciated.
From IBOBjects FAQ
Is there a way to detect a lost connection and try to reconnect
automatically, without user action?
Hook into the OnError event and look for the ERRCODE that denotes a
lost connection. Then, you can take whatever action you deem necessary
to deal with the problem. If the connection is lost you need to do a
disconnect and then connect again.
And from one of the base members of the IBObjects:
However, perhaps the "something" you are missing is that, if the
connection is broken by an external cause, the client application has
no way to know that it is not still connected. Its first knowledge of
that fact will come the next time it tries to access the server. The
API will report "Connection lost to database" and return GDSCODE
335544741, which is identified by the constant
isc_lost_db_connection.
At the point where this exception occurs, the TIB_Connection still
thinks it is connected - the Connected property will be true. If you
try to reconnect by calling Connect, you will get an IBO exception.
It is necessary to call Disconnect. This does not simply reset a
property. The Disconnect method performs all of the necessary cleanup
to invalidate the broken transactions and cancel any now invalid
postings, datasets and caches. Once Disconnect has completed its
work, you can then place a Connect call inside a retry loop and
attempt to get going again.
I do not know of a working example, but the simplest way to deal with
this is to write a RestoreConnection handler procedure that you can
call from your IB_Session.OnError handler whenever argument ERRCODE
returns isc_lost_db_connection.
Have your RestoreConnection procedure do whatever you need to do,
trying to call Connect and handling the exception that occurs if the
request fails, until no exception occurs. Test the Connected property
after each iteration. When Connected is finally True, you are in
business. You can drop out of the retry code and inform the user that
the connection has been restored - perhaps with a sound and/or a
message in the status bar, to avoid having to show a dialog box that
she has to respond to. (if you like the idea of sound and status bar
cues, you could devise "connection lost" warning sound and status bar
message code to run at the beginning of your handler procedure as
well...)
If these broken connections are a frequent occurrence, you might like
to consider making a distinctive custom cursor that you can display
while your procedure is running, and enclose the retry code in a
non-yielding BeginBusy...EndBusy block with UseCursor enabled and
BusyCursor set to use this special cursor image.
And if re-establishing a connection is likely to take a long time, or
to be temporarily impossible, you would need to provide the ability
for the user to intervene and choose not to keep trying. You can use
the session timer for this, enclosing your "busy" block inside
another iterative block the prompts the user to "Cancel" or "Keep
Trying", at reasonable intervals.
Source
Check out if their database file is located on mapped network drive. Even if database file path appear to be local to file system, when using embedded Firebird server, function isc_attach_database will return error code 335544721 on attempt to establish connection. Exactly that was happening on my VirtualBox guest Windows XP when I first share entire host D drive and then mapped it again as D drive in virtual guest OS.
Workaround will be to move database file to local partition drive.
check your query length
max query length is 8191 chars UTF-8
its solved my problem
connect your pc to the internet, and the problem will be fixed,but i don't how it works

Should I instantiate TIdSMTP each time email message is sent? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
All Indy SMTP examples I have seen show TIdSMTP being instantiated at the start of the email send routine and freed at the end.
I have written an SMTP email sender prototype application, based upon Indy 10 components, that normally works with no issues. (It can send in the clear or via explicit or implicit TLS.) It instantiates TIdSMTP on startup and frees it on closing.
I have just had a situation where IdSMTP.Send threw an exception saying that SMTP was not connected. This would be unremarkable except for the fact that I have a test for IdSMTP.Connected, immediately prior to my IdSMTP.Send, that reported that SMTP was connected. My program could no longer be made to send emails because it thought it was connected but in reality it wasn't. The only solution was to restart the program. When testing of this prototype application is completed the code will be incorporated into a server service that cannot just be restarted to fix this sort of problem.
The problem could have been avoided or, at least, worked around if I had instantiated my TIdSMTP class within my email send procedure; in the unlikely event of this re-occurring the IdSMTP object would be freed at the end of the procedure and re-instantiated the next time it gets called.
The reason I didn't architect my solution as in the examples was that re-instantiation of TIdSMTP for each email send also requires that the IdSMTP object reconnect to the email server for each send. This can be a very slow process - of the order of 5 to 10 seconds for external email servers - and this overhead can be avoided by not re-instantiating TIdSMTP each time.
So my question is: is this (or similar issues) the reason why all examples show re-instantiation each time? Or, is it simply to show a complete, contained example? The latter was how I interpreted it.
If you have experience with the pros and cons of this issue, please provide your opinions, and thoughts.
If there exists a definitive best practice, that would useful to hear about too.
All Indy SMTP examples I have seen show TIdSMTP being instantiated at the start of the email send routine and freed at the end.
They are just examples. Obviously production code can be more complex. You can reuse a single TIdSMTP object multiple times.
I have a test for IdSMTP.Connected, immediately prior to my IdSMTP.Send, that reported that SMTP was connected.
Let Send() fail and raise an exception if the connection is not available. Indy uses exceptions for error reporting, so make use of them, don't avoid them. Connected() can report a false positive. If the IOHandler.InputBuffer has unread data in it, Connected() will return true, even if the underlying socket is closed. This is by design. Don't rely on Connected() to drive your logic.
My program could no longer be made to send emails because it thought it was connected but in reality it wasn't.
After Connect() succeeds, if you get any exception that is not derived from EIdRFCReply, you should Disconnect() the connection and Clear() the InputBuffer (if there is still an IOHandler assigned - Disconnect() will free it if it was created internally by Connect()) before calling Connect() again. Connect() raises an exception if Connected() returns true, so you have to manually clear the condition that causes that (unread data) if an unexpected error occurs.
Try
If not SMTP.Connected then SMTP.Connect;
...
Except
On E: EIdRFCReply do
Begin
// an SMTP command failed, but the connection is still stable
...
End;
On E: Exception do
Begin
SMTP.Disconnect(False);
If SMTP.IOHandler <> nil then SMTP.IOHandler.InputBuffer.Clear;
...
End;
End;
In general, you should keep connections to the outside for as short a time as possible. I assume that this program/service doesn't send emails 24h/7d continously, but in response to some external event (a timer or other event that triggers "now it is time to send"), and then it sends one (or more) emails at that point.
The way I would code this is to instantiate the TIdSmtp at the start of this event and then free it at the end. This way, you would have a freshly instantiated connection to the outside every time.
If your program only sends a single email on every event, then you could perhaps code it such that if the sending fails, then signal that on the next event, you should re-instantiate the TIdSmtp variable, something along these lines:
PROCEDURE SendMsg(...)
BEGIN
IF NOT Assigned(SMTP) THEN SMTP:=TIdSmtp.Create(NIL);
TRY
... Your code to send one or more emails ...
EXCEPT
FreeAndNIL(SMTP);
... Perhaps Re-Raise exception, if outer layer needs to know ...
END
END;
This way, the SendMsg routine is self-restarting in case of error. Of course, some more error checking (what kind of exception was caught) would be advisable, but I expect you get the general idea...

Too many connections when developing

I'm trying (with a lot of help from this community) to put together my first client/server app.
I am using Indy 10 and Delphi Xe2, but suspect my problem does not lie with those, but rather with how soockets work (wizardy and black arts, if you ask me).
Because I often hit breakpoints of exceptions and step through my code before pressing Alt+F2 to halt, my next runs often hit exception "already connected" and eventually I get "too many connections".
How can I tidy this up?
(also, does anyone have a demo which uses try ... except, ratehr than try ... finally as in the Indy demos?)
"already connected" occurs on the client side when you call Connect() while Connected() still returns true. That usually occurs if you disconnect and leave unread data in the IOHandler.InputBuffer. Try clearing the InputBuffer before reconnecting. This is commonly encountered, so a near-future update to Indy may address that issue.
"too many connections" means that you set the server's MaxConnections property to a positive non-zero value and that many simultaneous clients are already connected to the server when a new client tries to connect. If you do not think that you are making that many simultaneous connections, then it usually means that you are not managing the connections correctly in your server code so disconnected clients get cleaned up correctly. The most common cause of that is putting exception handlers in your code that catch and swallow Indy's internal exceptions to itself. If you do catch exceptions, be sure to re-raise any that derive from EIdException and let the server handle them internally.

The connection does not timeout while using Indy

I want to download a file from internet and I imagine this should be a simple task. Trying several different approaches I have found that each one has its own drawback.
The main issues are:
Application freezes until it downloads the file
Application freezes forever if the Internet connection is lost/server does not respond.
(details:
How to retrieve a file from Internet via HTTP?
The connection does not timeout while downloading file from internet )
So, finally I used the suggestions I got from several people to use "pro" libraries such as Indy. However, Indy is not much better than the pieces of code I have tried (but it is way much larger and difficult to maintain). While using Indy the application does not freezes only for short periods so it is still (somehow) usable. However, the application cannot be shut down until the download finishes (never if the Internet connections gets broken).
Other people reported the same problem: http://borland.newsgroups.archived.at/public.delphi.internet.winsock/200609/0609079112.html
https://forums.embarcadero.com/thread.jspa?threadID=25199&tstart=90
So, there is some hacking I had to do to TIDAntiFreeze in order to make it work?
Also, the ConnectTimeout property is not recognized.
fIDHTTP := TIDHTTP.Create(NIL);
fIDHTTP.ConnectTimeout:=5000;
Should I drop Indy and return to original idea of downloading the file in a separate thread and end the thread when it does not respond (at least this way I get rid of 3rd party libraries)? There will be unforeseen side effects if I do this?
Using: Delphi 7, Indy 10.1.5 10.5 (probably).
Thanks
You probably need to use Indy the Indy way: using threads. Indy was specifically designed to work in blocking mode, because that's how most internet protocols work (example: with HTTP, at protocol level, you send a request, then you read the response. You don't send and receive at the same time). TIdAntiFreeze is supposed to help you use some Indy functionality without dealing with threads; I never used that because, at least conceptually, it's an ugly hack.
If you don't want to deal with threads then you should take a look at ICS - it was designed to be used in async mode, without threading. It doesn't need the equivalent of TIdAntiFreeze because it's not blocking. You start a download and you handle some events to get progress and completion notifications. ICS is just as well-known, professional and wildly used as Indy.
It's not too difficult to solve these sorts of problems. The first thing you have to do is make sure that you have properly handled error handling. If something fails then make sure everything cleans up properly. Beyond that make sure the downloading code is part of a separate thread. If there is any problem you can always terminate the thread from your main program. Here's the code (for downloading only, not the threading) which is working fine for me.
with TDownloadURL.Create(nil) do
try
URL := 'myurltodownload.com';
filename := 'locationtosaveto';
try
ExecuteTarget(nil);
except
result := false;
end;
if not FileExists(filename) then
result := false;
finally
clear;
free;
end;

Resources