 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Lesley Anne Guest
|
Posted: Tue Aug 29, 2006 11:01 pm Post subject: Would Indy's TIdTCPServer have any problem with connections |
|
|
I'm planning on adding a TIdTCPServer to one of my applications, but not
doing anything with it except occasionally turning on or off its Active
property. Another application will periodically connect to it, but no data
is sent between the two. I know the component will allocate buffers and
other resources, but would the fact that I don't plan on actually reading
from these connections ever cause problems with managing those resources?
(I've followed this pattern in other applications, but never using the Indy
library, so thought I should check.)
One other question about TIdTCPServer. The port it listens on is called
"DefaultPort". The use of the word "default" in the property's name is a
bit disturbing, as though it could end up listening on some other port
instead. Is that just a misnomer? From a server application, I need to be
able to trust what port it's listening on.
(If you're curious, the seemingly unused connections are there for alarming
purposes. Whenever my application is up and running smoothly, it will have
a server listening on a port. Whenever it runs into problems, it will shut
off that port. A separate alarming application periodically checks the
status of all our applications by seeing whether it can connect to their
alarming ports. Since status is reported simply by whether or not
connections are allowed, no further data needs to be sent.) |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Wed Aug 30, 2006 1:00 am Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
"Lesley Anne" <mspfila-ng (AT) yahoo (DOT) com> wrote in message
news:44f480ff (AT) newsgroups (DOT) borland.com...
| Quote: | I'm planning on adding a TIdTCPServer to one of my applications, but
not doing anything with it except occasionally turning on or off its
Active
property. Another application will periodically connect to it, but no
data
is sent between the two.
|
Interesting approach. I assume that you are doing this just so that one
application can detect if the other is running?
| Quote: | I know the component will allocate buffers and other resources, but
would the fact that I don't plan on actually reading from these
connections
ever cause problems with managing those resources?
|
The only problem with not doing any reading is that the server will never be
able to detect a client disconnecting. The server needs to perform some
kind of access to the socket, otherwise the socket will just sit there
indefinately. The server itself never checks the state of its client
connections. Instead, it relies on the OnExecute event handler to
disconnect the sockets, or otherwise perform reading/writing operations on
the sockets so that Indy can detect disconnects at the API layer, which will
then cause the reading/writing operations to throw an exception that the
server can use to terminate the managing thread and clean up the socket.
| Quote: | The port it listens on is called "DefaultPort".
|
Not exactly. The server listens on the Port of each item in the Bindings
collection. When adding a new item to the collection, the DefaultPort is
copied to the Bindings's Port property as the default value. The Port can
then be changed on a per-item basis before the server is activated, so the
server can listen on multiple ports at a time.
| Quote: | The use of the word "default" in the property's name is a bit disturbing
|
Only because you are thinking of it in the wrong context. See above.
| Quote: | as though it could end up listening on some other port instead.
|
It can. See above.
| Quote: | If you're curious, the seemingly unused connections are there for
alarming purposes. Whenever my application is up and running smoothly,
it will have a server listening on a port. Whenever it runs into
problems,
it will shut off that port. A separate alarming application periodically
checks the status of all our applications by seeing whether it can connect
to their alarming ports. Since status is reported simply by whether or
not connections are allowed, no further data needs to be sent.)
|
Are all of the applications running on a single machine? If so, then what
you describe is not really a good use of TCP/IP sockets. A named mutex that
is signaled during alarm conditions would probably work better. Of course,
if you need to check applications across a network, then using TCP/IP is
best, although I would suggest not closing the port at all. Leave it open,
and have the alarming application send a request over the connection. The
server can then report back whether an alarm exists, and better can also
report what the details of the alarm actually are.
Gambit |
|
| Back to top |
|
 |
Lesley Anne Guest
|
Posted: Wed Aug 30, 2006 10:10 pm Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
| Quote: | The server itself never checks the state of its client
connections. Instead, it relies on the OnExecute event handler
to disconnect the sockets, or otherwise perform reading/writing
operations on the sockets so that Indy can detect disconnects at
the API layer, which will then cause the reading/writing operations
to throw an exception that the server can use to terminate the
managing thread and clean up the socket.
|
So, if the disconnect will be coming from the client's end of the
connection, is the best way to make sure the component recognizes that
disconect to periodically try to read from all connections, trusting
that they'll error out correctly when the connection is no longer
active?
I thought at first that would be simple enough to do, but I'm having
a hard time finding how to even access all the (apparent) connections.
The best I could come up with to even see how many connections I have
is Server1->Bindings->Items->Collection->Count, but that seems like
more layers of properties than would generally be used for something
so fundamental, so I'm assuming I may have missed something. How would
you normally tell an application to do something with each connection
on a TIdTCPServer?
| Quote: | Are all of the applications running on a single machine? If so,
...
Of course, if you need to check applications across a network, then
using TCP/IP is best,
|
The applications are scattered across a network, with a centralized alarming
system.
| Quote: | although I would suggest not closing the port at all. Leave it open,
and have the alarming application send a request over the connection.
The server can then report back whether an alarm exists, and better
can also report what the details of the alarm actually are.
|
I agree that would be a better design, but at this point it's going against
the inertia of wanting to set up the alarming for this application the same
way that several prior applications were set up. Also, the alarming
application itself is now in the control of a different department who
monitors our platforms for us. It's easier to ask them to add a new address
and port to the list of platforms they check than to coordinate a new
protocol between our machine and theirs. |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Thu Aug 31, 2006 12:22 am Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
"Lesley Anne" <mspfila-ng (AT) yahoo (DOT) com> wrote in message
news:44f5c67a$1 (AT) newsgroups (DOT) borland.com...
| Quote: | So, if the disconnect will be coming from the client's end of the
connection, is the best way to make sure the component recognizes
that disconect to periodically try to read from all connections,
trusting that they'll error out correctly when the connection is no
longer active?
|
It is the only way to do it. Indy uses blocking sockets, and such sockets
must be read from, or written to, in order to detect a graceful disconnect
from the other party.
| Quote: | I thought at first that would be simple enough to do, but I'm having
a hard time finding how to even access all the (apparent) connections.
|
Why just just read inside the server's OnExecute event? It is triggered on
a per-connection basis.
If you must access the connections via a list, then you can reach them
through the server's Threads (Indy 9) or Contexts (Indy 10) property.
| Quote: | The best I could come up with to even see how many connections I
have is Server1->Bindings->Items->Collection->Count
|
That is not correct. The Bindings collection contains the listening sockets
that the server uses to accept incoming connections. The actual connections
are kept tracked of separately.
| Quote: | How would you normally tell an application to do something with
each connection on a TIdTCPServer?
|
You should be doing everything in the OnExecute event. But, if you must
access one or more connections outside of that, then you must go through the
server's Threads/Contexts property. By aware that TIdTCPServer is
multi-threaded, so make sure your reading/writing is thread-safe.
Gambit |
|
| Back to top |
|
 |
Lesley Anne Guest
|
Posted: Thu Aug 31, 2006 1:04 am Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
| Quote: | Why just just read inside the server's OnExecute event?
It is triggered on a per-connection basis.
|
When is that triggered? It sounded from the help file as though it were
triggered when a new thread is started, which I assume would be only when a
connection is first made. Is it triggered again after that?
And since the OnExecute event handler itself is in whichever thread the
TIdTCPServer component is in (the main application thread if the server is
on the form) rather than inside the individual thread that it references,
how do you keep one connection from blocking all others if that's where the
reading is done? |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Thu Aug 31, 2006 2:41 am Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
"Lesley Anne" <mspfila-ng (AT) yahoo (DOT) com> wrote in message
news:44f5ef4f (AT) newsgroups (DOT) borland.com...
| Quote: | Why just just read inside the server's OnExecute event?
It is triggered on a per-connection basis.
When is that triggered?
|
Immediately after the OnConnect event exits, OnExecute is triggered in a
continuous loop for the lifetime of the connection.
| Quote: | It sounded from the help file as though it were triggered when
a new thread is started
|
A new thread is created when a client connects to the server, yes.
OnConnect is triggered first, then OnExecute in a loop until the socket is
closed, and then OnDisconnect is triggered.
| Quote: | which I assume would be only when a connection is first made.
|
You assume incorrectly. That is not what the help file says.
| Quote: | Is it triggered again after that?
|
Yes. It is the main processing event for the thread. It is triggered in a
continuous loop while the thread is running.
| Quote: | the OnExecute event handler itself is in whichever thread the
TIdTCPServer component is in (the main application thread if
the server is on the form), rather than inside the individual thread
that it references
|
It does not matter what class the OnExecute event handler is DECLARED in.
The OnExecute event handler (and any other event handler that provides
access to the connection's thread/context) is TRIGGERED in the context of
the worker thread that is managing the connection. It is NOT triggered in
the context of the thread that owns the TIdTCPServer.
| Quote: | how do you keep one connection from blocking all others
|
By being multi-threaded, TIdTCPServer already does exactly that for you.
Just read inside the OnExecute event, and only that one connection will be
blocked. The other connections will trigger their own events separately.
Gambit |
|
| Back to top |
|
 |
Lesley Anne Guest
|
Posted: Thu Aug 31, 2006 8:51 pm Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
| Quote: | It does not matter what class the OnExecute event handler is
DECLARED in. The OnExecute event handler (and any other event
handler that provides access to the connection's thread/context)
is TRIGGERED in the context of the worker thread that is
managing the connection. It is NOT triggered in the context
of the thread that owns the TIdTCPServer.
|
Thanks for the explanation. (That's something that I wish the help file had
explained.) It works well here for reading, but now it looks like I'm going
to have to change my OnException and OnListenException handlers, if they
take place within their respective threads as well.
I've worked occasionally with secondary threads I wrote myself (as TThread
descendants), so in that type of case I know how to send messages from a
secondary thread back to my main thread using thread-global variables and
Synchronize(). I'm not sure how to do it here though, where I don't have
access to adding variables to the thread class. How would I send a String,
for instance, back to my main form from within one of these event handlers? |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Thu Aug 31, 2006 10:27 pm Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
"Lesley Anne" <mspfila-ng (AT) yahoo (DOT) com> wrote in message
news:44f70595$1 (AT) newsgroups (DOT) borland.com...
| Quote: | now it looks like I'm going to have to change my OnException and
OnListenException handlers, if they take place within their respective
threads as well.
|
Yes, they do. OnException is triggered in the context of the connection's
thread. OnListenException is triggered in the context of the thread that
TIdTCPServer creats for a particular Binding that is listening for incoming
connections.
| Quote: | I've worked occasionally with secondary threads I wrote myself (as
TThread descendants), so in that type of case I know how to send
messages from a secondary thread back to my main thread using
thread-global variables and Synchronize().
|
The same applies to Indy's threads as well. They are also TThread
descendants. Indy also provides other synchronization options, such as the
TIdSync and TIdNotify classes.
| Quote: | I don't have access to adding variables to the thread class.
|
Actually, you do. In Indy 9, simply derive from TIdPeerThread and then set
the server's ThreadClass property before activating the server.
TIdPeerThread is a TThread descendant, so you have access to its
Synchronize() method. For example:
class TMyPeerThread : public TIdPeerThread
{
private:
AnsiString FStr;
void __fastcall DoSendStr();
public:
__fastcall TMyPeerThread(bool ACreateSuspended);
void __fastcall SendStr(const AnsiString &AStr);
};
__fastcall TMyPeerThread::TMyPeerThread(bool ACreateSuspended)
: TIdPeerThread(ACreateSuspended)
{
}
void __fastcall TMyPeerThread::SendStr(const AnsiString &AStr)
{
FStr := AStr;
Synchronize(DoSendStr);
}
void __fastcall TMyPeerThread::DoSendStr()
{
// use FStr as needed ...
}
__fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
IdTCPServer1->ThreadClass = __classid(TMyPeerThread);
}
void __fastcall TForm1::IdTCPServer1Execute(TIdPeerThread *AThread)
{
//...
static_cast<TMyPeerThread*>(AThread)->SendStr("Hello World");
//...
}
In Indy 10, derive from TIdContext instead of TIdPeerThread. However,
TIdContext is not a TThread descendant, so you will have to use TIdSync when
you need to call Synchronize(). For example:
class TMyContext : public TIdContext
{
private:
AnsiString FStr;
void __fastcall DoSendStr;
public:
__fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn,
TIdThreadList *AList);
void __fastcall SendStr(const AnsiString &AStr);
};
__fastcall TMyContext::TMyContext(TIdTCPConnection *AConnection, TIdYarn
*AYarn, TIdThreadList *AList)
: TIdContext(AConnection, AYarn, AList)
{
}
void __fastcall TMyContext::SendStr(const AnsiString &AStr)
{
FStr := AStr;
TIdSync::SynchronizeMethod(__classid(TIdSync), &DoSendStr);
}
void __fastcall TMyContext::DoSendStr()
{
// use FStr as needed ...
}
__fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
IdTCPServer1->ContextClass = __classid(TMyContext);
}
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
//...
static_cast<TMyContext*>(AContext)->SendStr("Hello World");
//...
}
Alternatively, derive from TIdSync instead of TIdPeerThread/TIdContext, and
override the virtual DoSynchronize() method. This approach works (almost)
equally in both Indy 9 and 10. For example:
--- Indy 9 ---
class TMySync : public TIdSync
{
private:
AnsiString FStr;
public:
__fastcall TMySync(TIdThread *AThread, const AnsiString &AStr);
virtual void __fastcall DoSynchronize();
static void __fastcall SendStr(TIdThread *AThread, const AnsiString
&AStr);
};
__fastcall TMySync::TMySync(TIdThread *AThread, const AnsiString &AStr)
: TIdSync(AThread), FStr(AStr)
{
}
void __fastcall TMySync::SendStr(TIdThread *AThread, const AnsiString
&AStr)
{
TMySync *Sync = new TMySync(AThread, AStr);
try {
Sync->Synchronize();
}
__finally {
delete Sync;
}
}
void __fastcall TForm1::IdTCPServer1Execute(TIdPeerThread *AThread)
{
//...
TMySync::SendStr(AThread, "Hello World");
//...
}
--- Indy 10 ---
class TMySync : public TIdSync
{
private:
AnsiString FStr;
protected:
virtual void __fastcall DoSynchronize();
public:
__fastcall TMySync(const AnsiString &AStr);
static void __fastcall SendStr(const AnsiString &AStr);
};
__fastcall TMySync::TMySync(const AnsiString &AStr)
: TIdSync(), FStr(AStr)
{
}
void __fastcall TMySync::SendStr(const AnsiString &AStr)
{
TMySync *Sync = new TMySync(AStr);
try {
Sync->Synchronize();
}
__finally {
delete Sync;
}
}
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
//...
TMySync::SendStr("Hello World");
//...
}
Gambit |
|
| Back to top |
|
 |
Lesley Anne Guest
|
Posted: Fri Sep 01, 2006 12:48 am Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
| Quote: | OnException is triggered in the context of the connection's thread.
OnListenException is triggered in the context of the thread that
TIdTCPServer creats for a particular Binding that is listening for
incoming connections.
...
In Indy 9, simply derive from TIdPeerThread and then set the server's
ThreadClass property before activating the server. TIdPeerThread is a
TThread descendant, so you have access to its Synchronize() method.
...
Alternatively, derive from TIdSync instead of TIdPeerThread/TIdContext,
and override the virtual DoSynchronize() method.
|
Thanks again for the explanation and examples. The first method you
discussed, making a TIdPeerThread descendant, was clearer (maybe just
because it's more similar to other thread handling that I've done before).
I'd like to use that method, but it only seems to take care of the
TIdPeerThread cases. Since OnListenException takes place in the context of
a TIdListenerThread instead, would it not work there?
In your TIdSync descendant, I kind of got lost on what ended up happening to
the string. It had been loaded into Sync->FStr when you got to the line:
Sync->Synchronize();
but then I'm not entirely sure how to make Synchronize() do anything with
it. At some point it needs to reach my form where I can display it. So
would that go with something like:
void __fastcall TMySync::DoSynchronize()
{
Form1->HandleStringInput(FStr);
}
and:
void __fastcall TForm1::HandleStringInput(AnsiString Message)
{
Memo1->Lines->Add(Message);
}
(Actually, if I'm understanding this correctly, those could be combined into
just the DoSynchronize method, but I prefer keeping them separate just to
let TForm1 be where the decision is made of what to do with the message. I
think it looks clearer that way.)
Is that how this works, or am I (yet again) looking at this completely
wrong. Basically, will Sync->Synchronize() take care of whatever it needs
to do to make sure the threads actually are in synch, then call my
DoSynchronize() function in a context where it's safe to do so?
(Part of my problem in understanding this is that I don't really understand
quite what Synchronize() does in the first place. I know that it's
necessary, and a bit about why and when it's necessary, but that's about as
far as I got in understanding it. Not quite knowing how it does what it
needs to makes me a bit leery of overriding anything too closely connected
with it.) |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Fri Sep 01, 2006 2:08 am Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
"Lesley Anne" <mspfila-ng (AT) yahoo (DOT) com> wrote in message
news:44f73d33$1 (AT) newsgroups (DOT) borland.com...
| Quote: | I'd like to use that method, but it only seems to take care of the
TIdPeerThread cases. Since OnListenException takes place
in the context of a TIdListenerThread instead, would it not work
there?
|
There is nothing you can do about manipulating those threads. You will have
to use the TIdSync approach for that event instead. TIdSync has a secondary
constructor that does have a thread parameter. That constructor creates a
thread internal to the IdSync unit and is then used that for the call to
Synchronize().
Otherwise, re-design your code to not require Synchronized methods in the
first place. For instance, if all you are trying to do is log the error
messages, then write your logging code to be thread-safe, such as protecting
the log file with a critical section, and don't access the UI directly at
all. If you must use the GUI in your logging, then have the logging code
post strings to a thread-safe queue that the main thread can then display
perioically.
| Quote: | In your TIdSync descendant, I kind of got lost on what ended up
happening to the string.
|
It gets stored into the FStr member of the class, then TIdSync::Sychronize()
calls TThread::Synchronize(DoSynchronize) internally, and then
TMySync::DoSynchronize() uses FStr as needed.
At some point it needs to reach my form where I can display it.
That is what the overriden DoSynchronize() method is for.
| Quote: | So would that go with something like:
|
Yes.
| Quote: | Basically, will Sync->Synchronize() take care of whatever it needs
to do to make sure the threads actually are in synch, then call my
DoSynchronize() function in a context where it's safe to do so?
|
Yes.
| Quote: | Part of my problem in understanding this is that I don't really
understand quite what Synchronize() does in the first place.
|
See above.
Gambit |
|
| Back to top |
|
 |
Lesley Anne Guest
|
Posted: Fri Sep 01, 2006 7:40 pm Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
Thanks for all your help. I put in the TIdSync descendant, and I think I've
finally got this all working properly now.
 |
|
| Back to top |
|
 |
Joe Pasquariello Guest
|
Posted: Sun Sep 10, 2006 11:04 pm Post subject: Re: Would Indy's TIdTCPServer have any problem with connecti |
|
|
Hi Lesley,
It sounds like you've already got something working, but I thought I would
toss in a comment regarding communication between your app's main thread and
secondary threads. The conversations between you and Remy have been very
useful and informative. I've learned a lot about Indy be reading them, and
you are doing similar things to what I want to do. I've long been using
threads to encapsulate serial comm connections, and I don't use
Synchronize() to pass things back and forth, but rather I pass pointers to
structures back and forth via standard WIN32 message queue functions. An
earlier posting by Remy said that you would need to create your own message
queues, but they actually sort of create themselves. All you have to do is
call a function to read from a queue, and WIN32 will create the queue for
you. The Execute() function below shows how this works. The main thread uses
PostMessage() to write to secondary threads' message queues, and the
secondary threads use NotifyMessage() to post back to the main thread's
message queue. This makes all of the communication asynchronous. The nested
if statements implement a simple priority queue based on the message ID. All
messages to the thread contain a Transaction pointer, which is just an
application-specific structure that contains source, destination, data, etc.
The main thread allocates and deallocates the Transactions. One thing to be
aware of is that your secondary threads need a little time (less than a
second) for WIN32 to create their queues before you write to them. If you
call PostMessage() before the queue exists, that message will be lost and
you won't get a reply from the secondary thread. I'm using this method in
many applications, and it's so reliable that there is no need to have
timeouts in the main thread. If you write to a secondary thread, and you
implement whatever timeouts you want within those threads, the main thread
is guaranteed to get a response containing either the data you want or a
timeout indication.
Best regards,
Joe
void __fastcall TMyThread::Execute()
{
MSG msg, tmp;
Transaction *t;
for (; {
msg.message = 0;
while (!msg.message) {
if (!PeekMessage(&msg, NULL, FromClient4, FromClient4, PM_REMOVE))
if (!PeekMessage(&msg, NULL, FromClient3, FromClient3, PM_REMOVE))
if (!PeekMessage(&msg, NULL, FromClient2, FromClient2, PM_REMOVE))
if (!PeekMessage(&msg, NULL, FromClient1, FromClient1,
PM_REMOVE))
if (!PeekMessage(&msg, NULL, FromClient0, FromClient0,
PM_REMOVE))
if (!PeekMessage(&tmp, NULL, FromClient0, FromClient4,
PM_NOREMOVE))
WaitMessage();
}
t = (Transaction*)msg.lParam;
DoThreadWord( t );
SendNotifyMessage( (void*)t->client, FromMyThread, NULL, (long)t );
}
} |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|